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 9.6KB

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