canvas_lti_third_party_cookies 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1333ca5982732fe82a7282906c08a49efdc753d3b3ea2d87e927189917a71de
4
- data.tar.gz: 013f64730fdca2e2490b22d7b145ccc642037ad8c30c6893156958bc1686dd2c
3
+ metadata.gz: 8d232d4448ee62b75121b20f70143f70f75e0ea38a83408b3ed42bb51ce41247
4
+ data.tar.gz: 44605463e21036992d5e0dadc1ac7180384e3f4186917e0139fe586c9451c936
5
5
  SHA512:
6
- metadata.gz: 845e6dde86192f515409d5769c2018867384e032e866694e999b8b0d37f02cfd17e5e5e8a04720a66c58aa610a9fd29d2cd9e89b9441c1b5a8a1ff7b8674318a
7
- data.tar.gz: ce10870144e786fbc0c8100e1428aeaa449da381378ddd282fe8087fc40f225c5bef22a35e66ba46a780c2daeb86bf84161c14ff87727f69f17dc9eaf62b7901
6
+ metadata.gz: 65a832d626b6062a7f4982417681a267bab2d49a6d070e4aef5e99b28d947fdb9d22ba73fc51badc1ceb4a9a7b59e45b3f08f8998fb1c2a286d6ec3bebd440f9
7
+ data.tar.gz: 47b3da8ab13ac2b3b9e9fae11ccaba87344b2cce53d98edbe6f96250c37c54e98210b6d0345180adbd57e1990516c89f6eed8a6a9304a8abcd2e9ae44a9fe4dc
data/README.md CHANGED
@@ -1,52 +1,53 @@
1
1
  # canvas_lti_third_party_cookies
2
2
 
3
- Safari blocks all 3rd-party cookies by default, which breaks LTI tools that rely on setting cookies when launched in an iframe. Instead, it exposes a new API for getting user-permitted access to set cookies from an iframe, which works though requires jumping through some fun hoops.
4
- See this article for a detailed explanation: https://community.canvaslms.com/t5/Developers-Group/Safari-13-1-and-LTI-Integration/ba-p/273051
3
+ Safari blocks all 3rd-party cookies by default, which breaks LTI tools that rely on setting cookies when launched in an iframe. Instead, it exposes a new API for getting user-permitted access to set cookies from an iframe, which unfortunately doesn't completely work for LTI use cases.
4
+ See this article for a detailed explanation of the Storage Access API and previous attempts to launch LTI tools in Safari: https://community.canvaslms.com/t5/Developers-Group/Safari-13-1-and-LTI-Integration/ba-p/273051
5
5
 
6
- This gem wraps the Safari cookie dance in a Rails engine that can be easily used with one before_action callback. It is built for use with Canvas,
7
- which is responsible for some parts of this cookie dance, including launching the tool in a full-window 1st-party context, and providing a
8
- redirect url for the relaunch after the full-window launch is complete.
9
-
10
- This gem won't work without being launched from Canvas, or a Tool Consumer that implements the same `window.postMessage` listener as Canvas
6
+ The current workaround that this gem implements is to relaunch the tool in a new tab, new window, or popup window, where it can set first-party cookies to it's heart's content. This gem won't work without being launched from Canvas, or a Tool Consumer that implements the same `window.postMessage` listener as Canvas
11
7
  does here: https://github.com/instructure/canvas-lms/blob/master/public/javascripts/lti/post_message/requestFullWindowLaunch.js
12
8
 
9
+ ## Installation
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ # Set 3rd party cookies in Safari
14
+ gem 'canvas_lti_third_party_cookies'
15
+ ```
16
+
17
+ And then execute:
18
+ ```bash
19
+ $ bundle install
20
+ ```
21
+
13
22
  ## Usage
14
23
 
15
24
  Choose the Rails controller action that's used to launch your tool and set cookies. Set the before_action callback
16
25
  below to run on that action, and pass the data needed.
17
26
 
18
- * the `launch_url` parameter is required, which should be the route
19
- that launches the tool.
20
- * the `launch_params` parameter is optional, and should contain
21
- all needed query parameters that the tool requires to launch.
22
- * the `launch_data` parameter is optional, and should contain
23
- all needed form data that the tool requires to launch.
24
-
25
- Usually, only query parameters *or* form data is needed, not both.
27
+ * `placement`: (required) Should be the Canvas placement that the tool was launched from, taken from the decoded id_token's `https://www.instructure.com/placement` claim.
28
+ * `window_type`: (optional) Set to `:new_window` to open the tool in a new tab or window, or to `:popup` to open in a popup window.Defaults to `:new_window`.
29
+ * `width`: (optional) The width the popup window should be, in px. User has the discretion to ignore this. Only valid with window_type: popup. Defaults to 800px.
30
+ * `height`: (optional) The height the popup window should be, in px. User has the discretion to ignore this. Only valid with window_type: popup. Defaults to 600px.
26
31
 
27
32
  ```ruby
28
- include LtiThirdPartyCookies::SafariLaunch
33
+ include CanvasLtiThirdPartyCookies::SafariLaunch
29
34
  #...
30
35
  before_action -> {
31
- handle_safari_launch(launch_url: action_url, launch_params: { foo: bar }, launch_data: { foo: baz })
36
+ placement = decoded_id_token['https://www.instructure.com/placement']
37
+ handle_safari_launch(placement: placement, window_type: :popup)
32
38
  }
33
39
  ```
34
40
 
35
- ## Installation
36
- Add this line to your application's Gemfile:
37
-
38
- ```ruby
39
- # Set 3rd party cookies in Safari
40
- gem 'canvas_lti_third_party_cookies', '~> 0.2.0'
41
- ```
41
+ ## Testing
42
42
 
43
- And then execute:
44
43
  ```bash
45
- $ bundle install
44
+ $ rails test
46
45
  ```
47
46
 
48
- ## Testing
47
+ ## Publishing New Versions
49
48
 
50
- ```bash
51
- $ rails test
52
- ```
49
+ 1. Bump the version in `lib/canvas_lti_third_party_cookies/version.rb`.
50
+ 2. Commit, push, and merge that change.
51
+ 3. `rake install`
52
+ 4. `gem push pkg/canvas_lti_third_party_cookies-<version>.gem`
53
+ - note that this will only work if you have access
@@ -0,0 +1,60 @@
1
+ require 'browser'
2
+
3
+ module CanvasLtiThirdPartyCookies::SafariLaunch
4
+ extend ActiveSupport::Concern
5
+
6
+ # this needs to be called as a before_action on the route that launches the tool
7
+ # and the tool is required to pass some parameters to this method.
8
+ #
9
+ # `placement`: (required) Should be the Canvas placement that
10
+ # the tool was launched from, taken from the decoded id_token's
11
+ # `https://www.instructure.com/placement` claim.
12
+ #
13
+ # `window_type`: (optional) Set to `:new_window` to open the tool in a
14
+ # new tab or window, or to `:popup` to open in a popup window.
15
+ # Defaults to `:new_window`.
16
+ #
17
+ # `width`: (optional) The width the popup window should be, in px. User
18
+ # has the discretion to ignore this. Only valid with window_type: popup.
19
+ # Defaults to 800px.
20
+ #
21
+ # `height`: (optional) The height the popup window should be, in px. User
22
+ # has the discretion to ignore this. Only valid with window_type: popup.
23
+ # Defaults to 600px.
24
+ #
25
+ # example:
26
+ # include CanvasLtiThirdPartyCookies::SafariLaunch
27
+ # ...
28
+ # before_action -> {
29
+ # placement = decoded_id_token['https://www.instructure.com/placement']
30
+ # handle_safari_launch(placement: placement, window_type: :popup)
31
+ # }
32
+ def handle_safari_launch(placement:, window_type: :new_window, width: nil, height: nil)
33
+ raise ArgumentError.new("window_type must be either :new_window or :popup") unless [:new_window, :popup].include? window_type
34
+ return unless is_safari?
35
+ return if is_full_window_launch?
36
+
37
+ render(
38
+ 'canvas_lti_third_party_cookies/prep_for_full_window_launch',
39
+ locals: {
40
+ launch_url: request.base_url + request.fullpath,
41
+ placement: placement,
42
+ window_type: window_type.to_s,
43
+ width: width || 800,
44
+ height: height || 600
45
+ }
46
+ )
47
+ end
48
+
49
+ private
50
+
51
+ def is_full_window_launch?
52
+ params[:full_win_launch_requested].present?
53
+ end
54
+
55
+ def is_safari?
56
+ browser = Browser.new(request.headers["User-Agent"])
57
+ # detect both MacOS and iOS Safari
58
+ browser.safari? || (browser.webkit? && browser.platform.ios?)
59
+ end
60
+ end
@@ -1,36 +1,25 @@
1
1
  <%= javascript_tag do -%>
2
- const requestStorageAccess = () => {
3
- document
4
- .requestStorageAccess()
5
- .then(() => redirectToSetCookies())
6
- .catch(() => requestFullWindowLaunch());
7
- };
8
-
9
2
  const requestFullWindowLaunch = () => {
3
+ console.log("clicked")
10
4
  window.parent.postMessage(
11
5
  {
12
6
  messageType: "requestFullWindowLaunch",
13
- data: "<%= launch_url %>",
7
+ data: {
8
+ url: "<%= launch_url %>",
9
+ placement: "<%= placement %>",
10
+ launchType: "<%= window_type %>",
11
+ launchOptions: {
12
+ width: <%= width %>,
13
+ height: <%= height %>
14
+ }
15
+ }
14
16
  },
15
17
  "*"
16
18
  );
17
19
  };
18
-
19
- const redirectToSetCookies = () => {
20
- const form = document.getElementById("relaunch");
21
- form.submit();
22
- };
23
-
24
20
  document.addEventListener("DOMContentLoaded", () => {
25
- document.getElementById("request").addEventListener("click", requestStorageAccess)
26
- document
27
- .hasStorageAccess()
28
- .then((hasStorageAccess) => {
29
- if (hasStorageAccess) {
30
- redirectToSetCookies();
31
- }
32
- })
33
- .catch((err) => console.error(err));
21
+ console.log("loaded")
22
+ document.getElementById("request").addEventListener("click", requestFullWindowLaunch)
34
23
  });
35
24
  <% end %>
36
25
  <style type="text/css">
@@ -40,7 +29,7 @@
40
29
  height: 100%;
41
30
  font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
42
31
  font-size: 1.1em;
43
- padding: 0 75px 0 75px;
32
+ padding: 0 24px;
44
33
  }
45
34
 
46
35
  .flex-item {
@@ -48,8 +37,8 @@
48
37
  text-align: center;
49
38
  }
50
39
 
51
- .first {
52
- margin-top: 50px;
40
+ .flex-container div:first-child {
41
+ margin-top: 24px;
53
42
  }
54
43
 
55
44
  p {
@@ -87,7 +76,7 @@
87
76
  </style>
88
77
 
89
78
  <div class="flex-container">
90
- <div class="flex-item first">
79
+ <div class="flex-item">
91
80
  <img id="safari-logo" src="https://upload.wikimedia.org/wikipedia/commons/5/52/Safari_browser_logo.svg" alt="Safari Logo" />
92
81
  </div>
93
82
 
@@ -96,18 +85,15 @@
96
85
  </div>
97
86
 
98
87
  <div class="flex-item">
99
- <p>Safari requires your interaction with this app before logging you in.</p>
100
- <p>A dialog may appear asking you to allow this app to use cookies while browsing Canvas.</p>
101
- <p>For the best experience, click Allow.</p>
88
+ <p>Safari blocks third-party cookies by default, which this app relies on for sign-in features.</p>
89
+ <p>Launching this app in a <%if window_type == "popup"%>popup window<%else%>new tab<%end%>, separate from Canvas, will allow the app to set its own cookies.</p>
90
+ <%if window_type == "popup"%>
91
+ <p>If the app doesn't appear, make sure Safari is not blocking popups.</p>
92
+ <%end%>
102
93
  </div>
103
94
 
104
95
  <div class="flex-item">
105
- <button id="request">Continue to App</button>
96
+ <button id="request">Open <%if window_type == "popup"%>Popup<%else%>in New Tab<%end%></button>
106
97
  </div>
107
98
  <div>
108
- </div>
109
- <form id="relaunch" method="POST" action="<%= relaunch_url %>">
110
- <% launch_data.each do |key, value| -%>
111
- <%= hidden_field_tag key, value %>
112
- <% end -%>
113
- </form>
99
+ </div>
@@ -0,0 +1,4 @@
1
+ require "canvas_lti_third_party_cookies/engine"
2
+
3
+ module CanvasLtiThirdPartyCookies
4
+ end
@@ -0,0 +1,5 @@
1
+ module CanvasLtiThirdPartyCookies
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace CanvasLtiThirdPartyCookies
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module CanvasLtiThirdPartyCookies
2
+ VERSION = '0.4.0'
3
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canvas_lti_third_party_cookies
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xander Moffatt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-11 00:00:00.000000000 Z
11
+ date: 2021-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -75,13 +75,12 @@ files:
75
75
  - LICENSE
76
76
  - README.md
77
77
  - Rakefile
78
- - app/controllers/concerns/lti_third_party_cookies/safari_launch.rb
78
+ - app/controllers/concerns/canvas_lti_third_party_cookies/safari_launch.rb
79
+ - app/views/canvas_lti_third_party_cookies/prep_for_full_window_launch.erb
79
80
  - app/views/layouts/application.html.erb
80
- - app/views/lti_third_party_cookies/full_window_launch.erb
81
- - app/views/lti_third_party_cookies/request_storage_access.erb
82
- - lib/lti_third_party_cookies.rb
83
- - lib/lti_third_party_cookies/engine.rb
84
- - lib/lti_third_party_cookies/version.rb
81
+ - lib/canvas_lti_third_party_cookies.rb
82
+ - lib/canvas_lti_third_party_cookies/engine.rb
83
+ - lib/canvas_lti_third_party_cookies/version.rb
85
84
  homepage: https://gerrit.instructure.com/#/admin/projects/lti_third_party_cookies
86
85
  licenses:
87
86
  - MIT
@@ -1,66 +0,0 @@
1
- require 'browser'
2
-
3
- module LtiThirdPartyCookies::SafariLaunch
4
- extend ActiveSupport::Concern
5
-
6
- # this needs to be called as a before_action on the route that launches the tool
7
- # and the tool is required to pass some parameters to this method.
8
- # the `launch_url` parameter is required, which should be the route
9
- # that launches the tool.
10
- # the `launch_params` parameter is optional, and should contain
11
- # all needed query parameters that the tool requires to launch.
12
- # the `launch_data` parameter is optional, and should contain
13
- # all needed form data that the tool requires to launch.
14
- # example:
15
- # include LtiThirdPartyCookies::SafariLaunch
16
- # ...
17
- # before_action -> {
18
- # handle_safari_launch(launch_url: action_url, launch_params: { foo: bar }, launch_data: { foo: baz })
19
- # }
20
- def handle_safari_launch(launch_url:, launch_params: {}, launch_data: {})
21
- browser = Browser.new(request.headers["User-Agent"])
22
- # detect both MacOS and iOS Safari
23
- return unless browser.safari? || (browser.webkit? && browser.platform.ios?)
24
-
25
- # Safari launch #4: Storage Access has been granted,
26
- # so launch the app normally.
27
- if params[:storage_access_status].present?
28
- # remove from request directly instead of only from `params` so that
29
- # the tool *really* doesn't have to worry about this being present
30
- request.request_parameters.delete(:storage_access_status)
31
- return
32
- end
33
-
34
- # Safari launch #2: Full-window launch, solely for first-party user interaction.
35
- # During a full-window launch, Canvas provides a :platform_redirect_url that
36
- # will launch the tool again within an iframe in Canvas. (#3)
37
- if params[:platform_redirect_url].present?
38
- return render(
39
- 'lti_third_party_cookies/full_window_launch',
40
- locals: { platform_redirect_url: params[:platform_redirect_url] }
41
- )
42
- end
43
-
44
- # Safari launch #1: request Storage Access, then relaunch the tool. (#4)
45
- # If request fails, request a full window launch instead. (#2)
46
- # Safari launch #3: Relaunched by Canvas after full-window launch,
47
- # request Storage Access and then relaunch the tool. (#4)
48
- # Pass along any parameters provided by the tool that are needed to launch correctly,
49
- # and tell the tool that it has Storage Access.
50
- render(
51
- 'lti_third_party_cookies/request_storage_access',
52
- locals: {
53
- launch_url: launch_url,
54
- relaunch_url: relaunch_url(launch_url, launch_params),
55
- launch_data: launch_data.merge({ storage_access_status: "granted"})
56
- }
57
- )
58
- end
59
-
60
- private
61
-
62
- def relaunch_url(launch_url, launch_params)
63
- return launch_url if launch_params.empty?
64
- "#{launch_url}?#{launch_params.to_query}"
65
- end
66
- end
@@ -1,79 +0,0 @@
1
- <%= javascript_tag do -%>
2
- document.addEventListener("DOMContentLoaded", () => {
3
- document.getElementById("redirect").addEventListener("click", () => {
4
- window.location.replace("<%= platform_redirect_url %>");
5
- });
6
- });
7
- <% end %>
8
- <style type="text/css">
9
- .flex-container {
10
- display: flex;
11
- flex-direction: column;
12
- height: 100%;
13
- font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
14
- font-size: 1.1em;
15
- padding: 0 75px 0 75px;
16
- }
17
-
18
- .flex-item {
19
- margin-bottom: 10px;
20
- text-align: center;
21
- }
22
-
23
- .first {
24
- margin-top: 50px;
25
- }
26
-
27
- p {
28
- margin: 0 0 0.5em 0;
29
- }
30
-
31
- button {
32
- background: #008EE2;
33
- color: #ffffff;
34
- border: 1px solid;
35
- border-color: #0079C1;
36
- border-radius: 3px;
37
- transition: background-color 0.2s ease-in-out;
38
- display: inline-block;
39
- position: relative;
40
- padding: 8px 14px;
41
- margin-bottom: 0;
42
- font-size: 16px;
43
- font-size: 1rem;
44
- line-height: 20px;
45
- text-align: center;
46
- vertical-align: middle;
47
- cursor: pointer;
48
- text-decoration: none;
49
- overflow: hidden;
50
- text-shadow: none;
51
- -webkit-user-select: none;
52
- -moz-user-select: none;
53
- }
54
-
55
- #safari-logo {
56
- width: 100px;
57
- height: 100px;
58
- }
59
- </style>
60
-
61
- <div class="flex-container">
62
- <div class="flex-item first">
63
- <img id="safari-logo" src="https://upload.wikimedia.org/wikipedia/commons/5/52/Safari_browser_logo.svg" alt="Safari Logo" />
64
- </div>
65
-
66
- <div class="flex-item">
67
- <strong>It looks like you are using Safari.</strong>
68
- </div>
69
-
70
- <div class="flex-item">
71
- <p>Occasionally, Safari requires you to launch this app outside of Canvas before logging in.</p>
72
- <p>This setup is now complete, and Canvas can now relaunch this app.</p>
73
- </div>
74
-
75
- <div class="flex-item">
76
- <button id="redirect">Relaunch App in Canvas</button>
77
- </div>
78
- <div>
79
- </div>
@@ -1,5 +0,0 @@
1
- require "lti_third_party_cookies/engine"
2
- require_relative "../app/controllers/concerns/lti_third_party_cookies/safari_launch"
3
-
4
- module LtiThirdPartyCookies
5
- end
@@ -1,5 +0,0 @@
1
- module LtiThirdPartyCookies
2
- class Engine < ::Rails::Engine
3
- isolate_namespace LtiThirdPartyCookies
4
- end
5
- end
@@ -1,3 +0,0 @@
1
- module LtiThirdPartyCookies
2
- VERSION = '0.2.1'
3
- end