Merge remote-tracking branch 'pleroma/develop' into feature/disable-account
[akkoma] / test / web / oauth / oauth_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.OAuth.OAuthControllerTest do
6 use Pleroma.Web.ConnCase
7 import Pleroma.Factory
8 import Mock
9
10 alias Pleroma.Registration
11 alias Pleroma.Repo
12 alias Pleroma.Web.OAuth.Authorization
13 alias Pleroma.Web.OAuth.Token
14
15 @oauth_config_path [:oauth2, :issue_new_refresh_token]
16 @session_opts [
17 store: :cookie,
18 key: "_test",
19 signing_salt: "cooldude"
20 ]
21
22 describe "in OAuth consumer mode, " do
23 setup do
24 oauth_consumer_strategies_path = [:auth, :oauth_consumer_strategies]
25 oauth_consumer_strategies = Pleroma.Config.get(oauth_consumer_strategies_path)
26 Pleroma.Config.put(oauth_consumer_strategies_path, ~w(twitter facebook))
27
28 on_exit(fn ->
29 Pleroma.Config.put(oauth_consumer_strategies_path, oauth_consumer_strategies)
30 end)
31
32 [
33 app: insert(:oauth_app),
34 conn:
35 build_conn()
36 |> Plug.Session.call(Plug.Session.init(@session_opts))
37 |> fetch_session()
38 ]
39 end
40
41 test "GET /oauth/authorize renders auth forms, including OAuth consumer form", %{
42 app: app,
43 conn: conn
44 } do
45 conn =
46 get(
47 conn,
48 "/oauth/authorize",
49 %{
50 "response_type" => "code",
51 "client_id" => app.client_id,
52 "redirect_uri" => app.redirect_uris,
53 "scope" => "read"
54 }
55 )
56
57 assert response = html_response(conn, 200)
58 assert response =~ "Sign in with Twitter"
59 assert response =~ o_auth_path(conn, :prepare_request)
60 end
61
62 test "GET /oauth/prepare_request encodes parameters as `state` and redirects", %{
63 app: app,
64 conn: conn
65 } do
66 conn =
67 get(
68 conn,
69 "/oauth/prepare_request",
70 %{
71 "provider" => "twitter",
72 "authorization" => %{
73 "scope" => "read follow",
74 "client_id" => app.client_id,
75 "redirect_uri" => app.redirect_uris,
76 "state" => "a_state"
77 }
78 }
79 )
80
81 assert response = html_response(conn, 302)
82
83 redirect_query = URI.parse(redirected_to(conn)).query
84 assert %{"state" => state_param} = URI.decode_query(redirect_query)
85 assert {:ok, state_components} = Poison.decode(state_param)
86
87 expected_client_id = app.client_id
88 expected_redirect_uri = app.redirect_uris
89
90 assert %{
91 "scope" => "read follow",
92 "client_id" => ^expected_client_id,
93 "redirect_uri" => ^expected_redirect_uri,
94 "state" => "a_state"
95 } = state_components
96 end
97
98 test "with user-bound registration, GET /oauth/<provider>/callback redirects to `redirect_uri` with `code`",
99 %{app: app, conn: conn} do
100 registration = insert(:registration)
101
102 state_params = %{
103 "scope" => Enum.join(app.scopes, " "),
104 "client_id" => app.client_id,
105 "redirect_uri" => app.redirect_uris,
106 "state" => ""
107 }
108
109 with_mock Pleroma.Web.Auth.Authenticator,
110 get_registration: fn _ -> {:ok, registration} end do
111 conn =
112 get(
113 conn,
114 "/oauth/twitter/callback",
115 %{
116 "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
117 "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
118 "provider" => "twitter",
119 "state" => Poison.encode!(state_params)
120 }
121 )
122
123 assert response = html_response(conn, 302)
124 assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/
125 end
126 end
127
128 test "with user-unbound registration, GET /oauth/<provider>/callback renders registration_details page",
129 %{app: app, conn: conn} do
130 registration = insert(:registration, user: nil)
131
132 state_params = %{
133 "scope" => "read write",
134 "client_id" => app.client_id,
135 "redirect_uri" => app.redirect_uris,
136 "state" => "a_state"
137 }
138
139 with_mock Pleroma.Web.Auth.Authenticator,
140 get_registration: fn _ -> {:ok, registration} end do
141 conn =
142 get(
143 conn,
144 "/oauth/twitter/callback",
145 %{
146 "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
147 "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
148 "provider" => "twitter",
149 "state" => Poison.encode!(state_params)
150 }
151 )
152
153 assert response = html_response(conn, 200)
154 assert response =~ ~r/name="op" type="submit" value="register"/
155 assert response =~ ~r/name="op" type="submit" value="connect"/
156 assert response =~ Registration.email(registration)
157 assert response =~ Registration.nickname(registration)
158 end
159 end
160
161 test "on authentication error, GET /oauth/<provider>/callback redirects to `redirect_uri`", %{
162 app: app,
163 conn: conn
164 } do
165 state_params = %{
166 "scope" => Enum.join(app.scopes, " "),
167 "client_id" => app.client_id,
168 "redirect_uri" => app.redirect_uris,
169 "state" => ""
170 }
171
172 conn =
173 conn
174 |> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]})
175 |> get(
176 "/oauth/twitter/callback",
177 %{
178 "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
179 "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
180 "provider" => "twitter",
181 "state" => Poison.encode!(state_params)
182 }
183 )
184
185 assert response = html_response(conn, 302)
186 assert redirected_to(conn) == app.redirect_uris
187 assert get_flash(conn, :error) == "Failed to authenticate: (error description)."
188 end
189
190 test "GET /oauth/registration_details renders registration details form", %{
191 app: app,
192 conn: conn
193 } do
194 conn =
195 get(
196 conn,
197 "/oauth/registration_details",
198 %{
199 "authorization" => %{
200 "scopes" => app.scopes,
201 "client_id" => app.client_id,
202 "redirect_uri" => app.redirect_uris,
203 "state" => "a_state",
204 "nickname" => nil,
205 "email" => "john@doe.com"
206 }
207 }
208 )
209
210 assert response = html_response(conn, 200)
211 assert response =~ ~r/name="op" type="submit" value="register"/
212 assert response =~ ~r/name="op" type="submit" value="connect"/
213 end
214
215 test "with valid params, POST /oauth/register?op=register redirects to `redirect_uri` with `code`",
216 %{
217 app: app,
218 conn: conn
219 } do
220 registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil})
221
222 conn =
223 conn
224 |> put_session(:registration_id, registration.id)
225 |> post(
226 "/oauth/register",
227 %{
228 "op" => "register",
229 "authorization" => %{
230 "scopes" => app.scopes,
231 "client_id" => app.client_id,
232 "redirect_uri" => app.redirect_uris,
233 "state" => "a_state",
234 "nickname" => "availablenick",
235 "email" => "available@email.com"
236 }
237 }
238 )
239
240 assert response = html_response(conn, 302)
241 assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/
242 end
243
244 test "with invalid params, POST /oauth/register?op=register renders registration_details page",
245 %{
246 app: app,
247 conn: conn
248 } do
249 another_user = insert(:user)
250 registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil})
251
252 params = %{
253 "op" => "register",
254 "authorization" => %{
255 "scopes" => app.scopes,
256 "client_id" => app.client_id,
257 "redirect_uri" => app.redirect_uris,
258 "state" => "a_state",
259 "nickname" => "availablenickname",
260 "email" => "available@email.com"
261 }
262 }
263
264 for {bad_param, bad_param_value} <-
265 [{"nickname", another_user.nickname}, {"email", another_user.email}] do
266 bad_registration_attrs = %{
267 "authorization" => Map.put(params["authorization"], bad_param, bad_param_value)
268 }
269
270 bad_params = Map.merge(params, bad_registration_attrs)
271
272 conn =
273 conn
274 |> put_session(:registration_id, registration.id)
275 |> post("/oauth/register", bad_params)
276
277 assert html_response(conn, 403) =~ ~r/name="op" type="submit" value="register"/
278 assert get_flash(conn, :error) == "Error: #{bad_param} has already been taken."
279 end
280 end
281
282 test "with valid params, POST /oauth/register?op=connect redirects to `redirect_uri` with `code`",
283 %{
284 app: app,
285 conn: conn
286 } do
287 user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("testpassword"))
288 registration = insert(:registration, user: nil)
289
290 conn =
291 conn
292 |> put_session(:registration_id, registration.id)
293 |> post(
294 "/oauth/register",
295 %{
296 "op" => "connect",
297 "authorization" => %{
298 "scopes" => app.scopes,
299 "client_id" => app.client_id,
300 "redirect_uri" => app.redirect_uris,
301 "state" => "a_state",
302 "name" => user.nickname,
303 "password" => "testpassword"
304 }
305 }
306 )
307
308 assert response = html_response(conn, 302)
309 assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/
310 end
311
312 test "with invalid params, POST /oauth/register?op=connect renders registration_details page",
313 %{
314 app: app,
315 conn: conn
316 } do
317 user = insert(:user)
318 registration = insert(:registration, user: nil)
319
320 params = %{
321 "op" => "connect",
322 "authorization" => %{
323 "scopes" => app.scopes,
324 "client_id" => app.client_id,
325 "redirect_uri" => app.redirect_uris,
326 "state" => "a_state",
327 "name" => user.nickname,
328 "password" => "wrong password"
329 }
330 }
331
332 conn =
333 conn
334 |> put_session(:registration_id, registration.id)
335 |> post("/oauth/register", params)
336
337 assert html_response(conn, 401) =~ ~r/name="op" type="submit" value="connect"/
338 assert get_flash(conn, :error) == "Invalid Username/Password"
339 end
340 end
341
342 describe "GET /oauth/authorize" do
343 setup do
344 [
345 app: insert(:oauth_app, redirect_uris: "https://redirect.url"),
346 conn:
347 build_conn()
348 |> Plug.Session.call(Plug.Session.init(@session_opts))
349 |> fetch_session()
350 ]
351 end
352
353 test "renders authentication page", %{app: app, conn: conn} do
354 conn =
355 get(
356 conn,
357 "/oauth/authorize",
358 %{
359 "response_type" => "code",
360 "client_id" => app.client_id,
361 "redirect_uri" => app.redirect_uris,
362 "scope" => "read"
363 }
364 )
365
366 assert html_response(conn, 200) =~ ~s(type="submit")
367 end
368
369 test "properly handles internal calls with `authorization`-wrapped params", %{
370 app: app,
371 conn: conn
372 } do
373 conn =
374 get(
375 conn,
376 "/oauth/authorize",
377 %{
378 "authorization" => %{
379 "response_type" => "code",
380 "client_id" => app.client_id,
381 "redirect_uri" => app.redirect_uris,
382 "scope" => "read"
383 }
384 }
385 )
386
387 assert html_response(conn, 200) =~ ~s(type="submit")
388 end
389
390 test "renders authentication page if user is already authenticated but `force_login` is tru-ish",
391 %{app: app, conn: conn} do
392 token = insert(:oauth_token, app_id: app.id)
393
394 conn =
395 conn
396 |> put_session(:oauth_token, token.token)
397 |> get(
398 "/oauth/authorize",
399 %{
400 "response_type" => "code",
401 "client_id" => app.client_id,
402 "redirect_uri" => app.redirect_uris,
403 "scope" => "read",
404 "force_login" => "true"
405 }
406 )
407
408 assert html_response(conn, 200) =~ ~s(type="submit")
409 end
410
411 test "redirects to app if user is already authenticated", %{app: app, conn: conn} do
412 token = insert(:oauth_token, app_id: app.id)
413
414 conn =
415 conn
416 |> put_session(:oauth_token, token.token)
417 |> get(
418 "/oauth/authorize",
419 %{
420 "response_type" => "code",
421 "client_id" => app.client_id,
422 "redirect_uri" => app.redirect_uris,
423 "scope" => "read"
424 }
425 )
426
427 assert redirected_to(conn) == "https://redirect.url"
428 end
429 end
430
431 describe "POST /oauth/authorize" do
432 test "redirects with oauth authorization" do
433 user = insert(:user)
434 app = insert(:oauth_app, scopes: ["read", "write", "follow"])
435
436 conn =
437 build_conn()
438 |> post("/oauth/authorize", %{
439 "authorization" => %{
440 "name" => user.nickname,
441 "password" => "test",
442 "client_id" => app.client_id,
443 "redirect_uri" => app.redirect_uris,
444 "scope" => "read write",
445 "state" => "statepassed"
446 }
447 })
448
449 target = redirected_to(conn)
450 assert target =~ app.redirect_uris
451
452 query = URI.parse(target).query |> URI.query_decoder() |> Map.new()
453
454 assert %{"state" => "statepassed", "code" => code} = query
455 auth = Repo.get_by(Authorization, token: code)
456 assert auth
457 assert auth.scopes == ["read", "write"]
458 end
459
460 test "returns 401 for wrong credentials", %{conn: conn} do
461 user = insert(:user)
462 app = insert(:oauth_app)
463
464 result =
465 conn
466 |> post("/oauth/authorize", %{
467 "authorization" => %{
468 "name" => user.nickname,
469 "password" => "wrong",
470 "client_id" => app.client_id,
471 "redirect_uri" => app.redirect_uris,
472 "state" => "statepassed",
473 "scope" => Enum.join(app.scopes, " ")
474 }
475 })
476 |> html_response(:unauthorized)
477
478 # Keep the details
479 assert result =~ app.client_id
480 assert result =~ app.redirect_uris
481
482 # Error message
483 assert result =~ "Invalid Username/Password"
484 end
485
486 test "returns 401 for missing scopes", %{conn: conn} do
487 user = insert(:user)
488 app = insert(:oauth_app)
489
490 result =
491 conn
492 |> post("/oauth/authorize", %{
493 "authorization" => %{
494 "name" => user.nickname,
495 "password" => "test",
496 "client_id" => app.client_id,
497 "redirect_uri" => app.redirect_uris,
498 "state" => "statepassed",
499 "scope" => ""
500 }
501 })
502 |> html_response(:unauthorized)
503
504 # Keep the details
505 assert result =~ app.client_id
506 assert result =~ app.redirect_uris
507
508 # Error message
509 assert result =~ "This action is outside the authorized scopes"
510 end
511
512 test "returns 401 for scopes beyond app scopes", %{conn: conn} do
513 user = insert(:user)
514 app = insert(:oauth_app, scopes: ["read", "write"])
515
516 result =
517 conn
518 |> post("/oauth/authorize", %{
519 "authorization" => %{
520 "name" => user.nickname,
521 "password" => "test",
522 "client_id" => app.client_id,
523 "redirect_uri" => app.redirect_uris,
524 "state" => "statepassed",
525 "scope" => "read write follow"
526 }
527 })
528 |> html_response(:unauthorized)
529
530 # Keep the details
531 assert result =~ app.client_id
532 assert result =~ app.redirect_uris
533
534 # Error message
535 assert result =~ "This action is outside the authorized scopes"
536 end
537 end
538
539 describe "POST /oauth/token" do
540 test "issues a token for an all-body request" do
541 user = insert(:user)
542 app = insert(:oauth_app, scopes: ["read", "write"])
543
544 {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
545
546 conn =
547 build_conn()
548 |> post("/oauth/token", %{
549 "grant_type" => "authorization_code",
550 "code" => auth.token,
551 "redirect_uri" => app.redirect_uris,
552 "client_id" => app.client_id,
553 "client_secret" => app.client_secret
554 })
555
556 assert %{"access_token" => token, "me" => ap_id} = json_response(conn, 200)
557
558 token = Repo.get_by(Token, token: token)
559 assert token
560 assert token.scopes == auth.scopes
561 assert user.ap_id == ap_id
562 end
563
564 test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do
565 password = "testpassword"
566 user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
567
568 app = insert(:oauth_app, scopes: ["read", "write"])
569
570 # Note: "scope" param is intentionally omitted
571 conn =
572 build_conn()
573 |> post("/oauth/token", %{
574 "grant_type" => "password",
575 "username" => user.nickname,
576 "password" => password,
577 "client_id" => app.client_id,
578 "client_secret" => app.client_secret
579 })
580
581 assert %{"access_token" => token} = json_response(conn, 200)
582
583 token = Repo.get_by(Token, token: token)
584 assert token
585 assert token.scopes == app.scopes
586 end
587
588 test "issues a token for request with HTTP basic auth client credentials" do
589 user = insert(:user)
590 app = insert(:oauth_app, scopes: ["scope1", "scope2", "scope3"])
591
592 {:ok, auth} = Authorization.create_authorization(app, user, ["scope1", "scope2"])
593 assert auth.scopes == ["scope1", "scope2"]
594
595 app_encoded =
596 (URI.encode_www_form(app.client_id) <> ":" <> URI.encode_www_form(app.client_secret))
597 |> Base.encode64()
598
599 conn =
600 build_conn()
601 |> put_req_header("authorization", "Basic " <> app_encoded)
602 |> post("/oauth/token", %{
603 "grant_type" => "authorization_code",
604 "code" => auth.token,
605 "redirect_uri" => app.redirect_uris
606 })
607
608 assert %{"access_token" => token, "scope" => scope} = json_response(conn, 200)
609
610 assert scope == "scope1 scope2"
611
612 token = Repo.get_by(Token, token: token)
613 assert token
614 assert token.scopes == ["scope1", "scope2"]
615 end
616
617 test "rejects token exchange with invalid client credentials" do
618 user = insert(:user)
619 app = insert(:oauth_app)
620
621 {:ok, auth} = Authorization.create_authorization(app, user)
622
623 conn =
624 build_conn()
625 |> put_req_header("authorization", "Basic JTIxOiVGMCU5RiVBNCVCNwo=")
626 |> post("/oauth/token", %{
627 "grant_type" => "authorization_code",
628 "code" => auth.token,
629 "redirect_uri" => app.redirect_uris
630 })
631
632 assert resp = json_response(conn, 400)
633 assert %{"error" => _} = resp
634 refute Map.has_key?(resp, "access_token")
635 end
636
637 test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do
638 setting = Pleroma.Config.get([:instance, :account_activation_required])
639
640 unless setting do
641 Pleroma.Config.put([:instance, :account_activation_required], true)
642 on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
643 end
644
645 password = "testpassword"
646 user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
647 info_change = Pleroma.User.Info.confirmation_changeset(user.info, :unconfirmed)
648
649 {:ok, user} =
650 user
651 |> Ecto.Changeset.change()
652 |> Ecto.Changeset.put_embed(:info, info_change)
653 |> Repo.update()
654
655 refute Pleroma.User.auth_active?(user)
656
657 app = insert(:oauth_app)
658
659 conn =
660 build_conn()
661 |> post("/oauth/token", %{
662 "grant_type" => "password",
663 "username" => user.nickname,
664 "password" => password,
665 "client_id" => app.client_id,
666 "client_secret" => app.client_secret
667 })
668
669 assert resp = json_response(conn, 403)
670 assert %{"error" => _} = resp
671 refute Map.has_key?(resp, "access_token")
672 end
673
674 test "rejects token exchange for valid credentials belonging to deactivated user" do
675 password = "testpassword"
676
677 user =
678 insert(:user,
679 password_hash: Comeonin.Pbkdf2.hashpwsalt(password),
680 info: %{deactivated: true}
681 )
682
683 app = insert(:oauth_app)
684
685 conn =
686 build_conn()
687 |> post("/oauth/token", %{
688 "grant_type" => "password",
689 "username" => user.nickname,
690 "password" => password,
691 "client_id" => app.client_id,
692 "client_secret" => app.client_secret
693 })
694
695 assert resp = json_response(conn, 403)
696 assert %{"error" => _} = resp
697 refute Map.has_key?(resp, "access_token")
698 end
699
700 test "rejects an invalid authorization code" do
701 app = insert(:oauth_app)
702
703 conn =
704 build_conn()
705 |> post("/oauth/token", %{
706 "grant_type" => "authorization_code",
707 "code" => "Imobviouslyinvalid",
708 "redirect_uri" => app.redirect_uris,
709 "client_id" => app.client_id,
710 "client_secret" => app.client_secret
711 })
712
713 assert resp = json_response(conn, 400)
714 assert %{"error" => _} = json_response(conn, 400)
715 refute Map.has_key?(resp, "access_token")
716 end
717 end
718
719 describe "POST /oauth/token - refresh token" do
720 setup do
721 oauth_token_config = Pleroma.Config.get(@oauth_config_path)
722
723 on_exit(fn ->
724 Pleroma.Config.get(@oauth_config_path, oauth_token_config)
725 end)
726 end
727
728 test "issues a new access token with keep fresh token" do
729 Pleroma.Config.put(@oauth_config_path, true)
730 user = insert(:user)
731 app = insert(:oauth_app, scopes: ["read", "write"])
732
733 {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
734 {:ok, token} = Token.exchange_token(app, auth)
735
736 response =
737 build_conn()
738 |> post("/oauth/token", %{
739 "grant_type" => "refresh_token",
740 "refresh_token" => token.refresh_token,
741 "client_id" => app.client_id,
742 "client_secret" => app.client_secret
743 })
744 |> json_response(200)
745
746 ap_id = user.ap_id
747
748 assert match?(
749 %{
750 "scope" => "write",
751 "token_type" => "Bearer",
752 "expires_in" => 600,
753 "access_token" => _,
754 "refresh_token" => _,
755 "me" => ^ap_id
756 },
757 response
758 )
759
760 refute Repo.get_by(Token, token: token.token)
761 new_token = Repo.get_by(Token, token: response["access_token"])
762 assert new_token.refresh_token == token.refresh_token
763 assert new_token.scopes == auth.scopes
764 assert new_token.user_id == user.id
765 assert new_token.app_id == app.id
766 end
767
768 test "issues a new access token with new fresh token" do
769 Pleroma.Config.put(@oauth_config_path, false)
770 user = insert(:user)
771 app = insert(:oauth_app, scopes: ["read", "write"])
772
773 {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
774 {:ok, token} = Token.exchange_token(app, auth)
775
776 response =
777 build_conn()
778 |> post("/oauth/token", %{
779 "grant_type" => "refresh_token",
780 "refresh_token" => token.refresh_token,
781 "client_id" => app.client_id,
782 "client_secret" => app.client_secret
783 })
784 |> json_response(200)
785
786 ap_id = user.ap_id
787
788 assert match?(
789 %{
790 "scope" => "write",
791 "token_type" => "Bearer",
792 "expires_in" => 600,
793 "access_token" => _,
794 "refresh_token" => _,
795 "me" => ^ap_id
796 },
797 response
798 )
799
800 refute Repo.get_by(Token, token: token.token)
801 new_token = Repo.get_by(Token, token: response["access_token"])
802 refute new_token.refresh_token == token.refresh_token
803 assert new_token.scopes == auth.scopes
804 assert new_token.user_id == user.id
805 assert new_token.app_id == app.id
806 end
807
808 test "returns 400 if we try use access token" do
809 user = insert(:user)
810 app = insert(:oauth_app, scopes: ["read", "write"])
811
812 {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
813 {:ok, token} = Token.exchange_token(app, auth)
814
815 response =
816 build_conn()
817 |> post("/oauth/token", %{
818 "grant_type" => "refresh_token",
819 "refresh_token" => token.token,
820 "client_id" => app.client_id,
821 "client_secret" => app.client_secret
822 })
823 |> json_response(400)
824
825 assert %{"error" => "Invalid credentials"} == response
826 end
827
828 test "returns 400 if refresh_token invalid" do
829 app = insert(:oauth_app, scopes: ["read", "write"])
830
831 response =
832 build_conn()
833 |> post("/oauth/token", %{
834 "grant_type" => "refresh_token",
835 "refresh_token" => "token.refresh_token",
836 "client_id" => app.client_id,
837 "client_secret" => app.client_secret
838 })
839 |> json_response(400)
840
841 assert %{"error" => "Invalid credentials"} == response
842 end
843
844 test "issues a new token if token expired" do
845 user = insert(:user)
846 app = insert(:oauth_app, scopes: ["read", "write"])
847
848 {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
849 {:ok, token} = Token.exchange_token(app, auth)
850
851 change =
852 Ecto.Changeset.change(
853 token,
854 %{valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -86_400 * 30)}
855 )
856
857 {:ok, access_token} = Repo.update(change)
858
859 response =
860 build_conn()
861 |> post("/oauth/token", %{
862 "grant_type" => "refresh_token",
863 "refresh_token" => access_token.refresh_token,
864 "client_id" => app.client_id,
865 "client_secret" => app.client_secret
866 })
867 |> json_response(200)
868
869 ap_id = user.ap_id
870
871 assert match?(
872 %{
873 "scope" => "write",
874 "token_type" => "Bearer",
875 "expires_in" => 600,
876 "access_token" => _,
877 "refresh_token" => _,
878 "me" => ^ap_id
879 },
880 response
881 )
882
883 refute Repo.get_by(Token, token: token.token)
884 token = Repo.get_by(Token, token: response["access_token"])
885 assert token
886 assert token.scopes == auth.scopes
887 assert token.user_id == user.id
888 assert token.app_id == app.id
889 end
890 end
891
892 describe "POST /oauth/token - bad request" do
893 test "returns 500" do
894 response =
895 build_conn()
896 |> post("/oauth/token", %{})
897 |> json_response(500)
898
899 assert %{"error" => "Bad request"} == response
900 end
901 end
902
903 describe "POST /oauth/revoke - bad request" do
904 test "returns 500" do
905 response =
906 build_conn()
907 |> post("/oauth/revoke", %{})
908 |> json_response(500)
909
910 assert %{"error" => "Bad request"} == response
911 end
912 end
913 end