An IndieWeb engine for a self-hostable website. https://koype.net/
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.

token_api_controller_test.exs 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. defmodule Koype.Web.Indie.TokenApiControllerTest do
  2. use Koype.Web.ConnCase
  3. use Koype.DataCase
  4. import Mock
  5. @me "https://test.jacky"
  6. @client_id "https://client.test.jacky"
  7. @scope ~w(read create update)
  8. @redirect_uri "#{@client_id}/redirect"
  9. @code IndieWeb.Auth.Code.generate(@client_id, @redirect_uri)
  10. @route "/api/indie/token"
  11. @payload %{
  12. "grant_type" => "authorization_code",
  13. "code" => @code,
  14. "client_id" => @client_id,
  15. "redirect_uri" => @redirect_uri,
  16. "me" => @me
  17. }
  18. describe "POST .create/2" do
  19. test "200 confirms code" do
  20. with_mocks([
  21. {
  22. Koype,
  23. [],
  24. [links: fn -> %{} end, host: fn -> @me end, version: fn -> "not.a.version" end]
  25. },
  26. {
  27. IndieWeb.Auth.Code,
  28. [],
  29. [valid?: fn _code, _client, _redirect_uri -> :ok end]
  30. },
  31. {
  32. IndieWeb.Auth.Scope,
  33. [:passthrough],
  34. [fetch: fn _code -> {:ok, ~w(read)} end]
  35. }
  36. ]) do
  37. conn =
  38. build_conn()
  39. |> post(@route, @payload)
  40. assert json_response(conn, 200)
  41. assert %{"me" => @me} = json_response(conn, 200)
  42. assert %{"token_type" => "bearer"} = json_response(conn, 200)
  43. end
  44. end
  45. test "400 code is invalid" do
  46. with_mocks([
  47. {
  48. IndieWeb.Auth.Scope,
  49. [],
  50. [fetch: fn _code -> {:ok, nil} end]
  51. },
  52. {
  53. IndieWeb.Auth.Code,
  54. [],
  55. [valid?: fn _code, _client, _redirect -> {:error, :invalid_code} end, delete!: fn _ -> :ok end]
  56. },
  57. {
  58. Koype,
  59. [],
  60. [links: fn -> %{} end, host: fn -> @me end, version: fn -> "not.a.version" end]
  61. }
  62. ]) do
  63. conn = post(build_conn(), @route, @payload)
  64. assert json_response(conn, :forbidden)
  65. assert json_response(conn, :forbidden)["message"] =~ "code was not valid"
  66. end
  67. end
  68. test "400 scope missing for code" do
  69. with_mocks([
  70. {
  71. IndieWeb.Auth.Scope,
  72. [],
  73. [fetch: fn _code -> {:error, :scope_for_code_not_found} end]
  74. },
  75. {
  76. IndieWeb.Auth.Code,
  77. [],
  78. [valid?: fn _code, _client, _redirect -> :ok end, delete!: fn _ -> :ok end]
  79. },
  80. {
  81. Koype,
  82. [],
  83. [links: fn -> %{} end, host: fn -> @me end, version: fn -> "not.a.version" end]
  84. }
  85. ]) do
  86. conn = post(build_conn(), @route, @payload)
  87. assert json_response(conn, :forbidden)
  88. assert json_response(conn, :forbidden)["message"] =~ "scope"
  89. end
  90. end
  91. test "400 client_id does not match" do
  92. with_mocks([
  93. {
  94. IndieWeb.Auth.Code,
  95. [],
  96. [
  97. valid?: fn _code, _client, _redirect ->
  98. {:error, :mismatched_client_id_for_code}
  99. end
  100. ]
  101. },
  102. {
  103. Koype,
  104. [],
  105. [links: fn -> %{} end, host: fn -> @me end, version: fn -> "not.a.version" end]
  106. }
  107. ]) do
  108. conn =
  109. post(
  110. build_conn(),
  111. @route,
  112. Map.merge(
  113. @payload,
  114. %{
  115. client: "https://some.next.sitezt"
  116. }
  117. )
  118. )
  119. assert resp = json_response(conn, :forbidden)
  120. assert resp["message"] =~ "did not match the provided client id."
  121. end
  122. end
  123. test "400 redirect_uri does not match" do
  124. with_mocks([
  125. {
  126. IndieWeb.Auth.Code,
  127. [],
  128. [
  129. valid?: fn _code, _client, _redirect ->
  130. {:error, :mismatched_redirect_uri_for_code}
  131. end
  132. ]
  133. },
  134. {
  135. Koype,
  136. [],
  137. [links: fn -> %{} end, host: fn -> @me end, version: fn -> "not.a.version" end]
  138. }
  139. ]) do
  140. conn =
  141. post(
  142. build_conn(),
  143. @route,
  144. Map.merge(
  145. @payload,
  146. %{
  147. redirect_uri: "https://some.next.sitezt/redirect"
  148. }
  149. )
  150. )
  151. assert resp = json_response(conn, :forbidden)
  152. assert resp["message"] =~ "did not match the provided redirect URI."
  153. end
  154. end
  155. test "422 'me' is not handled for this server" do
  156. with_mocks([
  157. {
  158. IndieWeb.Auth.Code,
  159. [],
  160. [valid?: fn _code, _client, _redirect -> :ok end, delete!: fn _ -> :ok end]
  161. },
  162. {
  163. IndieWeb.Auth.Scope,
  164. [:passthrough],
  165. [fetch: fn _code -> {:ok, ~w(read)} end]
  166. },
  167. {
  168. Koype,
  169. [],
  170. [links: fn -> %{} end, host: fn -> @me end, version: fn -> "not.a.version" end]
  171. }
  172. ]) do
  173. conn =
  174. build_conn()
  175. |> post(
  176. @route,
  177. Map.put(@payload, "me", "https://some.next.sitezt")
  178. )
  179. assert resp = json_response(conn, :bad_request)
  180. assert resp["message"] =~ "provided is incorrect for this server"
  181. end
  182. end
  183. test "400 missing parameters" do
  184. conn =
  185. build_conn()
  186. |> post(@route, @payload |> Map.take(~w(me)))
  187. assert json_response(conn, :bad_request)
  188. end
  189. test "200 revokes token" do
  190. token = Koype.Web.AuthenticationHelpers.indie_sign_in(@client_id, @scope)
  191. payload = %{"action" => "revoke", "token" => token}
  192. conn =
  193. build_conn()
  194. |> post(@route, payload)
  195. assert text_response(conn, :ok) == "ok"
  196. end
  197. end
  198. describe "GET .validate/2" do
  199. test "200 handles authorization from header" do
  200. conn =
  201. build_conn()
  202. |> indie_sign_in_conn(@client_id, @scope)
  203. |> get(@route)
  204. assert resp = json_response(conn, 200)
  205. resp_keys = Map.keys(resp)
  206. ~w(me client_id scope)
  207. |> Enum.each(fn key ->
  208. assert Enum.member?(resp_keys, key), "missing key '#{key}' for response"
  209. end)
  210. end
  211. test "401 fails if token is not valid" do
  212. conn =
  213. build_conn()
  214. |> put_req_header("authorization", "bearer bwahahaha_test")
  215. |> get(@route)
  216. assert json_response(conn, :unauthorized)
  217. end
  218. test "400 fails if token is not valid" do
  219. conn =
  220. build_conn()
  221. |> put_req_header("authorization", "bearer bwahahaha_test")
  222. |> get(@route)
  223. assert json_response(conn, :unauthorized)
  224. end
  225. end
  226. end