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