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.

243 lines
9.0 KiB

  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package integrations
  5. import (
  6. "encoding/json"
  7. "testing"
  8. "code.gitea.io/gitea/modules/setting"
  9. "github.com/stretchr/testify/assert"
  10. )
  11. const defaultAuthorize = "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=a&response_type=code&state=thestate"
  12. func TestNoClientID(t *testing.T) {
  13. prepareTestEnv(t)
  14. req := NewRequest(t, "GET", "/login/oauth/authorize")
  15. ctx := loginUser(t, "user2")
  16. ctx.MakeRequest(t, req, 400)
  17. }
  18. func TestLoginRedirect(t *testing.T) {
  19. prepareTestEnv(t)
  20. req := NewRequest(t, "GET", "/login/oauth/authorize")
  21. assert.Contains(t, MakeRequest(t, req, 302).Body.String(), "/user/login")
  22. }
  23. func TestShowAuthorize(t *testing.T) {
  24. prepareTestEnv(t)
  25. req := NewRequest(t, "GET", defaultAuthorize)
  26. ctx := loginUser(t, "user4")
  27. resp := ctx.MakeRequest(t, req, 200)
  28. htmlDoc := NewHTMLParser(t, resp.Body)
  29. htmlDoc.AssertElement(t, "#authorize-app", true)
  30. htmlDoc.GetCSRF()
  31. }
  32. func TestRedirectWithExistingGrant(t *testing.T) {
  33. prepareTestEnv(t)
  34. req := NewRequest(t, "GET", defaultAuthorize)
  35. ctx := loginUser(t, "user1")
  36. resp := ctx.MakeRequest(t, req, 302)
  37. u, err := resp.Result().Location()
  38. assert.NoError(t, err)
  39. assert.Equal(t, "thestate", u.Query().Get("state"))
  40. assert.Truef(t, len(u.Query().Get("code")) > 30, "authorization code '%s' should be longer then 30", u.Query().Get("code"))
  41. }
  42. func TestAccessTokenExchange(t *testing.T) {
  43. prepareTestEnv(t)
  44. req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  45. "grant_type": "authorization_code",
  46. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  47. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  48. "redirect_uri": "a",
  49. "code": "authcode",
  50. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  51. })
  52. resp := MakeRequest(t, req, 200)
  53. type response struct {
  54. AccessToken string `json:"access_token"`
  55. TokenType string `json:"token_type"`
  56. ExpiresIn int64 `json:"expires_in"`
  57. RefreshToken string `json:"refresh_token"`
  58. }
  59. parsed := new(response)
  60. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed))
  61. assert.True(t, len(parsed.AccessToken) > 10)
  62. assert.True(t, len(parsed.RefreshToken) > 10)
  63. }
  64. func TestAccessTokenExchangeWithoutPKCE(t *testing.T) {
  65. prepareTestEnv(t)
  66. req := NewRequestWithJSON(t, "POST", "/login/oauth/access_token", map[string]string{
  67. "grant_type": "authorization_code",
  68. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  69. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  70. "redirect_uri": "a",
  71. "code": "authcode",
  72. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  73. })
  74. resp := MakeRequest(t, req, 200)
  75. type response struct {
  76. AccessToken string `json:"access_token"`
  77. TokenType string `json:"token_type"`
  78. ExpiresIn int64 `json:"expires_in"`
  79. RefreshToken string `json:"refresh_token"`
  80. }
  81. parsed := new(response)
  82. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed))
  83. assert.True(t, len(parsed.AccessToken) > 10)
  84. assert.True(t, len(parsed.RefreshToken) > 10)
  85. }
  86. func TestAccessTokenExchangeJSON(t *testing.T) {
  87. prepareTestEnv(t)
  88. req := NewRequestWithJSON(t, "POST", "/login/oauth/access_token", map[string]string{
  89. "grant_type": "authorization_code",
  90. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  91. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  92. "redirect_uri": "a",
  93. "code": "authcode",
  94. })
  95. MakeRequest(t, req, 400)
  96. }
  97. func TestAccessTokenExchangeWithInvalidCredentials(t *testing.T) {
  98. prepareTestEnv(t)
  99. // invalid client id
  100. req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  101. "grant_type": "authorization_code",
  102. "client_id": "???",
  103. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  104. "redirect_uri": "a",
  105. "code": "authcode",
  106. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  107. })
  108. MakeRequest(t, req, 400)
  109. // invalid client secret
  110. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  111. "grant_type": "authorization_code",
  112. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  113. "client_secret": "???",
  114. "redirect_uri": "a",
  115. "code": "authcode",
  116. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  117. })
  118. MakeRequest(t, req, 400)
  119. // invalid redirect uri
  120. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  121. "grant_type": "authorization_code",
  122. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  123. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  124. "redirect_uri": "???",
  125. "code": "authcode",
  126. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  127. })
  128. MakeRequest(t, req, 400)
  129. // invalid authorization code
  130. req = NewRequestWithValues(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": "???",
  136. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  137. })
  138. MakeRequest(t, req, 400)
  139. // invalid grant_type
  140. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  141. "grant_type": "???",
  142. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  143. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  144. "redirect_uri": "a",
  145. "code": "authcode",
  146. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  147. })
  148. MakeRequest(t, req, 400)
  149. }
  150. func TestAccessTokenExchangeWithBasicAuth(t *testing.T) {
  151. prepareTestEnv(t)
  152. req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  153. "grant_type": "authorization_code",
  154. "redirect_uri": "a",
  155. "code": "authcode",
  156. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  157. })
  158. req.Header.Add("Authorization", "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpKaUE9")
  159. resp := MakeRequest(t, req, 200)
  160. type response struct {
  161. AccessToken string `json:"access_token"`
  162. TokenType string `json:"token_type"`
  163. ExpiresIn int64 `json:"expires_in"`
  164. RefreshToken string `json:"refresh_token"`
  165. }
  166. parsed := new(response)
  167. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed))
  168. assert.True(t, len(parsed.AccessToken) > 10)
  169. assert.True(t, len(parsed.RefreshToken) > 10)
  170. // use wrong client_secret
  171. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  172. "grant_type": "authorization_code",
  173. "redirect_uri": "a",
  174. "code": "authcode",
  175. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  176. })
  177. req.Header.Add("Authorization", "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OmJsYWJsYQ==")
  178. resp = MakeRequest(t, req, 400)
  179. // missing header
  180. req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  181. "grant_type": "authorization_code",
  182. "redirect_uri": "a",
  183. "code": "authcode",
  184. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  185. })
  186. resp = MakeRequest(t, req, 400)
  187. }
  188. func TestRefreshTokenInvalidation(t *testing.T) {
  189. prepareTestEnv(t)
  190. req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  191. "grant_type": "authorization_code",
  192. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  193. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  194. "redirect_uri": "a",
  195. "code": "authcode",
  196. "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", // test PKCE additionally
  197. })
  198. resp := MakeRequest(t, req, 200)
  199. type response struct {
  200. AccessToken string `json:"access_token"`
  201. TokenType string `json:"token_type"`
  202. ExpiresIn int64 `json:"expires_in"`
  203. RefreshToken string `json:"refresh_token"`
  204. }
  205. parsed := new(response)
  206. assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed))
  207. // test without invalidation
  208. setting.OAuth2.InvalidateRefreshTokens = false
  209. refreshReq := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{
  210. "grant_type": "refresh_token",
  211. "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138",
  212. "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=",
  213. "redirect_uri": "a",
  214. "refresh_token": parsed.RefreshToken,
  215. })
  216. MakeRequest(t, refreshReq, 200)
  217. MakeRequest(t, refreshReq, 200)
  218. // test with invalidation
  219. setting.OAuth2.InvalidateRefreshTokens = true
  220. MakeRequest(t, refreshReq, 200)
  221. MakeRequest(t, refreshReq, 400)
  222. }