You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

oauth_test.go 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "bytes"
  6. "io"
  7. "net/http"
  8. "testing"
  9. "code.gitea.io/gitea/modules/json"
  10. "code.gitea.io/gitea/modules/setting"
  11. "code.gitea.io/gitea/routers/web/auth"
  12. "code.gitea.io/gitea/tests"
  13. "github.com/stretchr/testify/assert"
  14. )
  15. func TestAuthorizeNoClientID(t *testing.T) {
  16. defer tests.PrepareTestEnv(t)()
  17. req := NewRequest(t, "GET", "/login/oauth/authorize")
  18. ctx := loginUser(t, "user2")
  19. resp := ctx.MakeRequest(t, req, http.StatusBadRequest)
  20. assert.Contains(t, resp.Body.String(), "Client ID not registered")
  21. }
  22. func TestAuthorizeUnregisteredRedirect(t *testing.T) {
  23. defer tests.PrepareTestEnv(t)()
  24. req := NewRequest(t, "GET", "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=UNREGISTERED&response_type=code&state=thestate")
  25. ctx := loginUser(t, "user1")
  26. resp := ctx.MakeRequest(t, req, http.StatusBadRequest)
  27. assert.Contains(t, resp.Body.String(), "Unregistered Redirect URI")
  28. }
  29. func TestAuthorizeUnsupportedResponseType(t *testing.T) {
  30. defer tests.PrepareTestEnv(t)()
  31. req := NewRequest(t, "GET", "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=a&response_type=UNEXPECTED&state=thestate")
  32. ctx := loginUser(t, "user1")
  33. resp := ctx.MakeRequest(t, req, http.StatusSeeOther)
  34. u, err := resp.Result().Location()
  35. assert.NoError(t, err)
  36. assert.Equal(t, "unsupported_response_type", u.Query().Get("error"))
  37. assert.Equal(t, "Only code response type is supported.", u.Query().Get("error_description"))
  38. }
  39. func TestAuthorizeUnsupportedCodeChallengeMethod(t *testing.T) {
  40. defer tests.PrepareTestEnv(t)()
  41. req := NewRequest(t, "GET", "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=a&response_type=code&state=thestate&code_challenge_method=UNEXPECTED")
  42. ctx := loginUser(t, "user1")
  43. resp := ctx.MakeRequest(t, req, http.StatusSeeOther)
  44. u, err := resp.Result().Location()
  45. assert.NoError(t, err)
  46. assert.Equal(t, "invalid_request", u.Query().Get("error"))
  47. assert.Equal(t, "unsupported code challenge method", u.Query().Get("error_description"))
  48. }
  49. func TestAuthorizeLoginRedirect(t *testing.T) {
  50. defer tests.PrepareTestEnv(t)()
  51. req := NewRequest(t, "GET", "/login/oauth/authorize")
  52. assert.Contains(t, MakeRequest(t, req, http.StatusSeeOther).Body.String(), "/user/login")
  53. }
  54. func TestAuthorizeShow(t *testing.T) {
  55. defer tests.PrepareTestEnv(t)()
  56. req := NewRequest(t, "GET", "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=a&response_type=code&state=thestate")
  57. ctx := loginUser(t, "user4")
  58. resp := ctx.MakeRequest(t, req, http.StatusOK)
  59. htmlDoc := NewHTMLParser(t, resp.Body)
  60. htmlDoc.AssertElement(t, "#authorize-app", true)
  61. htmlDoc.GetCSRF()
  62. }
  63. func TestAuthorizeRedirectWithExistingGrant(t *testing.T) {
  64. defer tests.PrepareTestEnv(t)()
  65. req := NewRequest(t, "GET", "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=https%3A%2F%2Fexample.com%2Fxyzzy&response_type=code&state=thestate")
  66. ctx := loginUser(t, "user1")
  67. resp := ctx.MakeRequest(t, req, http.StatusSeeOther)
  68. u, err := resp.Result().Location()
  69. assert.NoError(t, err)
  70. assert.Equal(t, "thestate", u.Query().Get("state"))
  71. assert.Truef(t, len(u.Query().Get("code")) > 30, "authorization code '%s' should be longer then 30", u.Query().Get("code"))
  72. u.RawQuery = ""
  73. assert.Equal(t, "https://example.com/xyzzy", u.String())
  74. }
  75. func TestAuthorizePKCERequiredForPublicClient(t *testing.T) {
  76. defer tests.PrepareTestEnv(t)()
  77. req := NewRequest(t, "GET", "/login/oauth/authorize?client_id=ce5a1322-42a7-11ed-b878-0242ac120002&redirect_uri=http%3A%2F%2F127.0.0.1&response_type=code&state=thestate")
  78. ctx := loginUser(t, "user1")
  79. resp := ctx.MakeRequest(t, req, http.StatusSeeOther)
  80. u, err := resp.Result().Location()
  81. assert.NoError(t, err)
  82. assert.Equal(t, "invalid_request", u.Query().Get("error"))
  83. assert.Equal(t, "PKCE is required for public clients", u.Query().Get("error_description"))
  84. }
  85. func TestAccessTokenExchange(t *testing.T) {
  86. defer tests.PrepareTestEnv(t)()
  87. req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  88. "grant_type": "authorization_code",
  89. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  90. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  91. "redirect_uri": "a",
  92. "code": "authcode",
  93. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  94. })
  95. resp := MakeRequest(t, req, http.StatusOK)
  96. type response struct {
  97. AccessToken string `json:"access_token"`
  98. TokenType string `json:"token_type"`
  99. ExpiresIn int64 `json:"expires_in"`
  100. RefreshToken string `json:"refresh_token"`
  101. }
  102. parsed := new(response)
  103. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed))
  104. assert.True(t, len(parsed.AccessToken) > 10)
  105. assert.True(t, len(parsed.RefreshToken) > 10)
  106. }
  107. func TestAccessTokenExchangeWithPublicClient(t *testing.T) {
  108. defer tests.PrepareTestEnv(t)()
  109. req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  110. "grant_type": "authorization_code",
  111. "client_id": "ce5a1322-42a7-11ed-b878-0242ac120002",
  112. "redirect_uri": "http://127.0.0.1",
  113. "code": "authcodepublic",
  114. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  115. })
  116. resp := MakeRequest(t, req, http.StatusOK)
  117. type response struct {
  118. AccessToken string `json:"access_token"`
  119. TokenType string `json:"token_type"`
  120. ExpiresIn int64 `json:"expires_in"`
  121. RefreshToken string `json:"refresh_token"`
  122. }
  123. parsed := new(response)
  124. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed))
  125. assert.True(t, len(parsed.AccessToken) > 10)
  126. assert.True(t, len(parsed.RefreshToken) > 10)
  127. }
  128. func TestAccessTokenExchangeJSON(t *testing.T) {
  129. defer tests.PrepareTestEnv(t)()
  130. req := NewRequestWithJSON(t, "POST", "/login/oauth/access_token", map[string]string{
  131. "grant_type": "authorization_code",
  132. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  133. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  134. "redirect_uri": "a",
  135. "code": "authcode",
  136. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  137. })
  138. resp := MakeRequest(t, req, http.StatusOK)
  139. type response struct {
  140. AccessToken string `json:"access_token"`
  141. TokenType string `json:"token_type"`
  142. ExpiresIn int64 `json:"expires_in"`
  143. RefreshToken string `json:"refresh_token"`
  144. }
  145. parsed := new(response)
  146. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed))
  147. assert.True(t, len(parsed.AccessToken) > 10)
  148. assert.True(t, len(parsed.RefreshToken) > 10)
  149. }
  150. func TestAccessTokenExchangeWithoutPKCE(t *testing.T) {
  151. defer tests.PrepareTestEnv(t)()
  152. req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  153. "grant_type": "authorization_code",
  154. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  155. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  156. "redirect_uri": "a",
  157. "code": "authcode",
  158. })
  159. resp := MakeRequest(t, req, http.StatusBadRequest)
  160. parsedError := new(auth.AccessTokenError)
  161. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  162. assert.Equal(t, "unauthorized_client", string(parsedError.ErrorCode))
  163. assert.Equal(t, "failed PKCE code challenge", parsedError.ErrorDescription)
  164. }
  165. func TestAccessTokenExchangeWithInvalidCredentials(t *testing.T) {
  166. defer tests.PrepareTestEnv(t)()
  167. // invalid client id
  168. req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  169. "grant_type": "authorization_code",
  170. "client_id": "???",
  171. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  172. "redirect_uri": "a",
  173. "code": "authcode",
  174. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  175. })
  176. resp := MakeRequest(t, req, http.StatusBadRequest)
  177. parsedError := new(auth.AccessTokenError)
  178. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  179. assert.Equal(t, "invalid_client", string(parsedError.ErrorCode))
  180. assert.Equal(t, "cannot load client with client id: '???'", parsedError.ErrorDescription)
  181. // invalid client secret
  182. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  183. "grant_type": "authorization_code",
  184. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  185. "client_secret": "???",
  186. "redirect_uri": "a",
  187. "code": "authcode",
  188. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  189. })
  190. resp = MakeRequest(t, req, http.StatusBadRequest)
  191. parsedError = new(auth.AccessTokenError)
  192. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  193. assert.Equal(t, "unauthorized_client", string(parsedError.ErrorCode))
  194. assert.Equal(t, "invalid client secret", parsedError.ErrorDescription)
  195. // invalid redirect uri
  196. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  197. "grant_type": "authorization_code",
  198. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  199. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  200. "redirect_uri": "???",
  201. "code": "authcode",
  202. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  203. })
  204. resp = MakeRequest(t, req, http.StatusBadRequest)
  205. parsedError = new(auth.AccessTokenError)
  206. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  207. assert.Equal(t, "unauthorized_client", string(parsedError.ErrorCode))
  208. assert.Equal(t, "unexpected redirect URI", parsedError.ErrorDescription)
  209. // invalid authorization code
  210. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  211. "grant_type": "authorization_code",
  212. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  213. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  214. "redirect_uri": "a",
  215. "code": "???",
  216. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  217. })
  218. resp = MakeRequest(t, req, http.StatusBadRequest)
  219. parsedError = new(auth.AccessTokenError)
  220. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  221. assert.Equal(t, "unauthorized_client", string(parsedError.ErrorCode))
  222. assert.Equal(t, "client is not authorized", parsedError.ErrorDescription)
  223. // invalid grant_type
  224. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  225. "grant_type": "???",
  226. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  227. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  228. "redirect_uri": "a",
  229. "code": "authcode",
  230. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  231. })
  232. resp = MakeRequest(t, req, http.StatusBadRequest)
  233. parsedError = new(auth.AccessTokenError)
  234. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  235. assert.Equal(t, "unsupported_grant_type", string(parsedError.ErrorCode))
  236. assert.Equal(t, "Only refresh_token or authorization_code grant type is supported", parsedError.ErrorDescription)
  237. }
  238. func TestAccessTokenExchangeWithBasicAuth(t *testing.T) {
  239. defer tests.PrepareTestEnv(t)()
  240. req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  241. "grant_type": "authorization_code",
  242. "redirect_uri": "a",
  243. "code": "authcode",
  244. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  245. })
  246. req.Header.Add("Authorization", "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpKaUE9")
  247. resp := MakeRequest(t, req, http.StatusOK)
  248. type response struct {
  249. AccessToken string `json:"access_token"`
  250. TokenType string `json:"token_type"`
  251. ExpiresIn int64 `json:"expires_in"`
  252. RefreshToken string `json:"refresh_token"`
  253. }
  254. parsed := new(response)
  255. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed))
  256. assert.True(t, len(parsed.AccessToken) > 10)
  257. assert.True(t, len(parsed.RefreshToken) > 10)
  258. // use wrong client_secret
  259. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  260. "grant_type": "authorization_code",
  261. "redirect_uri": "a",
  262. "code": "authcode",
  263. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  264. })
  265. req.Header.Add("Authorization", "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OmJsYWJsYQ==")
  266. resp = MakeRequest(t, req, http.StatusBadRequest)
  267. parsedError := new(auth.AccessTokenError)
  268. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  269. assert.Equal(t, "unauthorized_client", string(parsedError.ErrorCode))
  270. assert.Equal(t, "invalid client secret", parsedError.ErrorDescription)
  271. // missing header
  272. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  273. "grant_type": "authorization_code",
  274. "redirect_uri": "a",
  275. "code": "authcode",
  276. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  277. })
  278. resp = MakeRequest(t, req, http.StatusBadRequest)
  279. parsedError = new(auth.AccessTokenError)
  280. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  281. assert.Equal(t, "invalid_client", string(parsedError.ErrorCode))
  282. assert.Equal(t, "cannot load client with client id: ''", parsedError.ErrorDescription)
  283. // client_id inconsistent with Authorization header
  284. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  285. "grant_type": "authorization_code",
  286. "redirect_uri": "a",
  287. "code": "authcode",
  288. "client_id": "inconsistent",
  289. })
  290. req.Header.Add("Authorization", "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpKaUE9")
  291. resp = MakeRequest(t, req, http.StatusBadRequest)
  292. parsedError = new(auth.AccessTokenError)
  293. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  294. assert.Equal(t, "invalid_request", string(parsedError.ErrorCode))
  295. assert.Equal(t, "client_id in request body inconsistent with Authorization header", parsedError.ErrorDescription)
  296. // client_secret inconsistent with Authorization header
  297. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  298. "grant_type": "authorization_code",
  299. "redirect_uri": "a",
  300. "code": "authcode",
  301. "client_secret": "inconsistent",
  302. })
  303. req.Header.Add("Authorization", "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpKaUE9")
  304. resp = MakeRequest(t, req, http.StatusBadRequest)
  305. parsedError = new(auth.AccessTokenError)
  306. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  307. assert.Equal(t, "invalid_request", string(parsedError.ErrorCode))
  308. assert.Equal(t, "client_secret in request body inconsistent with Authorization header", parsedError.ErrorDescription)
  309. }
  310. func TestRefreshTokenInvalidation(t *testing.T) {
  311. defer tests.PrepareTestEnv(t)()
  312. req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  313. "grant_type": "authorization_code",
  314. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  315. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  316. "redirect_uri": "a",
  317. "code": "authcode",
  318. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt",
  319. })
  320. resp := MakeRequest(t, req, http.StatusOK)
  321. type response struct {
  322. AccessToken string `json:"access_token"`
  323. TokenType string `json:"token_type"`
  324. ExpiresIn int64 `json:"expires_in"`
  325. RefreshToken string `json:"refresh_token"`
  326. }
  327. parsed := new(response)
  328. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed))
  329. // test without invalidation
  330. setting.OAuth2.InvalidateRefreshTokens = false
  331. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  332. "grant_type": "refresh_token",
  333. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  334. // omit secret
  335. "redirect_uri": "a",
  336. "refresh_token": parsed.RefreshToken,
  337. })
  338. resp = MakeRequest(t, req, http.StatusBadRequest)
  339. parsedError := new(auth.AccessTokenError)
  340. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  341. assert.Equal(t, "invalid_client", string(parsedError.ErrorCode))
  342. assert.Equal(t, "invalid empty client secret", parsedError.ErrorDescription)
  343. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  344. "grant_type": "refresh_token",
  345. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  346. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  347. "redirect_uri": "a",
  348. "refresh_token": "UNEXPECTED",
  349. })
  350. resp = MakeRequest(t, req, http.StatusBadRequest)
  351. parsedError = new(auth.AccessTokenError)
  352. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  353. assert.Equal(t, "unauthorized_client", string(parsedError.ErrorCode))
  354. assert.Equal(t, "unable to parse refresh token", parsedError.ErrorDescription)
  355. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  356. "grant_type": "refresh_token",
  357. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  358. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  359. "redirect_uri": "a",
  360. "refresh_token": parsed.RefreshToken,
  361. })
  362. bs, err := io.ReadAll(req.Body)
  363. assert.NoError(t, err)
  364. req.Body = io.NopCloser(bytes.NewReader(bs))
  365. MakeRequest(t, req, http.StatusOK)
  366. req.Body = io.NopCloser(bytes.NewReader(bs))
  367. MakeRequest(t, req, http.StatusOK)
  368. // test with invalidation
  369. setting.OAuth2.InvalidateRefreshTokens = true
  370. req.Body = io.NopCloser(bytes.NewReader(bs))
  371. MakeRequest(t, req, http.StatusOK)
  372. // repeat request should fail
  373. req.Body = io.NopCloser(bytes.NewReader(bs))
  374. resp = MakeRequest(t, req, http.StatusBadRequest)
  375. parsedError = new(auth.AccessTokenError)
  376. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsedError))
  377. assert.Equal(t, "unauthorized_client", string(parsedError.ErrorCode))
  378. assert.Equal(t, "token was already used", parsedError.ErrorDescription)
  379. }