canvas_lti_third_party_cookies 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 18685ffb4c05fd45f61a6e2e739fe93b4b2e9e8573e72eb07bb8d026cdb7889d
4
+ data.tar.gz: 1cf0160c9d86c39975abb6b64f24ca518ea052625738c904876103ad565a6e01
5
+ SHA512:
6
+ metadata.gz: e770cba91ab52316550435e6f1bac4708b292b834f73ea5028da56ec92f6d86d7107f7f0f77c6c0e8da9bc8624b59cc40c837ca32cf0cc19e4215ff164dad249
7
+ data.tar.gz: 3a3f448a4ed8ff5e401d913c913207943576c5335c10299d29aad5c08859f581f5e58f2e42bb98cc0aa44ad080f5f41b99d58971ba0546e41c70ba0eeb8653b3
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2020 Instructure
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,52 @@
1
+ # canvas_lti_third_party_cookies
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
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
11
+ does here: https://github.com/instructure/canvas-lms/blob/master/public/javascripts/lti/post_message/requestFullWindowLaunch.js
12
+
13
+ ## Usage
14
+
15
+ Choose the Rails controller action that's used to launch your tool and set cookies. Set the before_action callback
16
+ below to run on that action, and pass the data needed.
17
+
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.
26
+
27
+ ```ruby
28
+ include LtiThirdPartyCookies::SafariLaunch
29
+ #...
30
+ before_action -> {
31
+ handle_safari_launch(launch_url: action_url, launch_params: { foo: bar }, launch_data: { foo: baz })
32
+ }
33
+ ```
34
+
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
+ ```
42
+
43
+ And then execute:
44
+ ```bash
45
+ $ bundle install
46
+ ```
47
+
48
+ ## Testing
49
+
50
+ ```bash
51
+ $ rails test
52
+ ```
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Lti::Third::Party::Cookies'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1,66 @@
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
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>LTI Tool</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+ </head>
8
+
9
+ <body>
10
+ <%= yield %>
11
+ </body>
12
+ </html>
@@ -0,0 +1,79 @@
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>
@@ -0,0 +1,113 @@
1
+ <%= javascript_tag do -%>
2
+ const requestStorageAccess = () => {
3
+ document
4
+ .requestStorageAccess()
5
+ .then(() => redirectToSetCookies())
6
+ .catch(() => requestFullWindowLaunch());
7
+ };
8
+
9
+ const requestFullWindowLaunch = () => {
10
+ window.parent.postMessage(
11
+ {
12
+ messageType: "requestFullWindowLaunch",
13
+ data: "<%= launch_url %>",
14
+ },
15
+ "*"
16
+ );
17
+ };
18
+
19
+ const redirectToSetCookies = () => {
20
+ const form = document.getElementById("relaunch");
21
+ form.submit();
22
+ };
23
+
24
+ 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));
34
+ });
35
+ <% end %>
36
+ <style type="text/css">
37
+ .flex-container {
38
+ display: flex;
39
+ flex-direction: column;
40
+ height: 100%;
41
+ font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
42
+ font-size: 1.1em;
43
+ padding: 0 75px 0 75px;
44
+ }
45
+
46
+ .flex-item {
47
+ margin-bottom: 10px;
48
+ text-align: center;
49
+ }
50
+
51
+ .first {
52
+ margin-top: 50px;
53
+ }
54
+
55
+ p {
56
+ margin: 0 0 0.5em 0;
57
+ }
58
+
59
+ button {
60
+ background: #008EE2;
61
+ color: #ffffff;
62
+ border: 1px solid;
63
+ border-color: #0079C1;
64
+ border-radius: 3px;
65
+ transition: background-color 0.2s ease-in-out;
66
+ display: inline-block;
67
+ position: relative;
68
+ padding: 8px 14px;
69
+ margin-bottom: 0;
70
+ font-size: 16px;
71
+ font-size: 1rem;
72
+ line-height: 20px;
73
+ text-align: center;
74
+ vertical-align: middle;
75
+ cursor: pointer;
76
+ text-decoration: none;
77
+ overflow: hidden;
78
+ text-shadow: none;
79
+ -webkit-user-select: none;
80
+ -moz-user-select: none;
81
+ }
82
+
83
+ #safari-logo {
84
+ width: 100px;
85
+ height: 100px;
86
+ }
87
+ </style>
88
+
89
+ <div class="flex-container">
90
+ <div class="flex-item first">
91
+ <img id="safari-logo" src="https://upload.wikimedia.org/wikipedia/commons/5/52/Safari_browser_logo.svg" alt="Safari Logo" />
92
+ </div>
93
+
94
+ <div class="flex-item">
95
+ <strong>It looks like you are using Safari.</strong>
96
+ </div>
97
+
98
+ <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>
102
+ </div>
103
+
104
+ <div class="flex-item">
105
+ <button id="request">Continue to App</button>
106
+ </div>
107
+ <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>
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,4 @@
1
+ require "lti_third_party_cookies/engine"
2
+
3
+ module LtiThirdPartyCookies
4
+ end
@@ -0,0 +1,5 @@
1
+ module LtiThirdPartyCookies
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace LtiThirdPartyCookies
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module LtiThirdPartyCookies
2
+ VERSION = '0.2.0'
3
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: canvas_lti_third_party_cookies
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Xander Moffatt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-12-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.2
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 6.0.2.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 6.0.2
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 6.0.2.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: browser
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.6'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.6.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '2.6'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 2.6.1
53
+ - !ruby/object:Gem::Dependency
54
+ name: sqlite3
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ description: Contains the redirects and user interactions needed for an LTI tool to
68
+ launch from Canvas and set cookies in Safari 13.1+
69
+ email:
70
+ - xmoffatt@instructure.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - LICENSE
76
+ - README.md
77
+ - Rakefile
78
+ - app/controllers/concerns/lti_third_party_cookies/safari_launch.rb
79
+ - 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
+ - config/routes.rb
83
+ - lib/lti_third_party_cookies.rb
84
+ - lib/lti_third_party_cookies/engine.rb
85
+ - lib/lti_third_party_cookies/version.rb
86
+ homepage: https://gerrit.instructure.com/#/admin/projects/lti_third_party_cookies
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubygems_version: 3.0.1
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: Allow LTI tools launched by Canvas to set 3rd party cookies in Safari 13.1+
109
+ test_files: []