Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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