canvas_lti_third_party_cookies 0.3.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 890865e3f76d11ffcb3c1e203b7b507c068127b0b2cba1455fdf0fa558ef96f8
4
- data.tar.gz: 197439ea71f9dd99b21235358022f7c324f86fdec4e03ce93c7f28201dfc2d83
3
+ metadata.gz: bfadd9bf819df5cfb7740b3b1c88f7436337b0bafbc243d9df216af7e948cd9e
4
+ data.tar.gz: 6a539c02d809cbffbc9693a69567942af8ba24d839299d0baca93e3ebe1ef676
5
5
  SHA512:
6
- metadata.gz: 8c7001cbfb01825b8a475ed48f15cfdd3e962ce5fcda789f1a211a1445c5d75e8f171982a7e7c4ed954e43a22f393f781be9a9e15fbfee97122e7f8438cac9d6
7
- data.tar.gz: 8a4a17fc88a70d26b57ea6862bdbd9f71bd2ec21cfa548e42868d4dec3167be6d24471c29ed346e03dfa8900ff6b4e08849084519160ce8f78f5a8a927bd59b5
6
+ metadata.gz: a346aec85ae07519abe21496dbec0126bab0ab6ffc96de0d0b7c23be9eea02080cde3b7949295b598e9827c597442d83f866c758f0790a5bfad4da4faa4821cb
7
+ data.tar.gz: 9fb2fbc396790aca7e388e430adcd36ff2b2acf5d3f94309413e732b1ad354fdac8ee76197f1b3e74948ac59731cc10cbd3c931968361d0d6d3176c0e4af8a73
data/README.md CHANGED
@@ -1,46 +1,9 @@
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. Other browsers will soon follow suit. 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
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 CanvasLtiThirdPartyCookies::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
- This will launch the tool multiple times, and also redirect the user back to Canvas when needed. For more information on the detailed tool
36
- launches, see the comments in `app/controllers/concerns/canvas_lti_third_party_cookies/safari_launch.rb`.
37
-
38
- Note that the tool will be relaunched from within this method once Storage Access is granted and pass all parameters from the previous
39
- Canvas launch, which will break JWT nonce verification since it will detect the nonce has already been used.
40
-
41
- To combat this, this gem provides the `should_ignore_nonce?` method so that your tool can ignore the nonce verification for that
42
- specific launch. Normally, ignoring a duplicate nonce can lead to replay attacks. This method will only return true if the request's
43
- `Referer` header matches the tool's domain, which only happens in this last internal redirect.
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. The relaunch occurs during the login request, so the tool is entirely in control, and no other parts of the LTI launch flow are modified.
44
7
 
45
8
  ## Installation
46
9
  Add this line to your application's Gemfile:
@@ -55,6 +18,48 @@ And then execute:
55
18
  $ bundle install
56
19
  ```
57
20
 
21
+ ## Usage
22
+
23
+ Choose the Rails controller action that's used to launch your tool and set cookies. Set the before_action callback
24
+ below to run on that action, and pass the data needed.
25
+
26
+ * `redirect_url` (required): the authorization redirect URL to continue the login flow
27
+ * `redirect_data` (required): all form data required for the authorization redirect, which
28
+ the previous login render call should have included in a form tag.
29
+ * `window_type`: (optional) Set to `:new_window` to open the tool in a
30
+ new tab or window, or to `:popup` to open in a popup window.
31
+ Defaults to `:new_window`.
32
+ * `width`: (optional) The width the popup window should be, in px. User
33
+ has the discretion to ignore this. Only valid with window_type: popup.
34
+ Defaults to 800px.
35
+ * `height`: (optional) The height the popup window should be, in px. User
36
+ has the discretion to ignore this. Only valid with window_type: popup.
37
+ Defaults to 600px.
38
+
39
+ example:
40
+ ```ruby
41
+ include CanvasLtiThirdPartyCookies::RelaunchOnLogin
42
+ ...
43
+ def login
44
+ state, nonce = create_and_cache_state # handled elsewhere
45
+ redirect_url = 'http://canvas.instructure.com/api/lti/authorize_redirect'
46
+ redirect_data = {
47
+ scope: 'openid',
48
+ response_type: 'id_token',
49
+ response_mode: 'form_post',
50
+ prompt: 'none',
51
+ redirect_uri: redirect_uri, # the launch url of the tool
52
+ client_id: params.require(:client_id),
53
+ login_hint: params.require(:login_hint),
54
+ lti_message_hint: params.require(:lti_message_hint),
55
+ state: state,
56
+ nonce: nonce
57
+ }
58
+
59
+ relaunch_on_login(redirect_url, redirect_data)
60
+ end
61
+ ```
62
+
58
63
  ## Testing
59
64
 
60
65
  ```bash
@@ -64,7 +69,7 @@ $ rails test
64
69
  ## Publishing New Versions
65
70
 
66
71
  1. Bump the version in `lib/canvas_lti_third_party_cookies/version.rb`.
67
- 2. Commit, push, and merge that change.
68
- 3. `rake install`
72
+ 2. `rake install`
73
+ 3. Commit, push, and merge that change.
69
74
  4. `gem push pkg/canvas_lti_third_party_cookies-<version>.gem`
70
75
  - note that this will only work if you have access
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
@@ -0,0 +1,66 @@
1
+ document.addEventListener("DOMContentLoaded", () => {
2
+ const { window_type, form_target, width, height } = window.ENV;
3
+
4
+ let newWindow;
5
+ const openNewWindow = () => {
6
+ switch (window_type) {
7
+ case "popup": {
8
+ newWindow = window.open(
9
+ "",
10
+ form_target,
11
+ "toolbar=no,menubar=no,location=no,status=no,resizable,scrollbars," +
12
+ `width=${width},height=${height}`
13
+ );
14
+ break;
15
+ }
16
+ case "new_window": {
17
+ newWindow = window.open("", form_target);
18
+ break;
19
+ }
20
+ }
21
+ };
22
+
23
+ const getRelaunchElement = (id) => document.getElementById(`relaunch-${id}`);
24
+ const hide = (el) => (el.style.display = "none");
25
+ const show = (el) => (el.style.display = "flex"); // normally this is 'block', but the containers need to be 'flex'
26
+
27
+ const successMessageEl = getRelaunchElement("success");
28
+ const retryMessageEl = getRelaunchElement("retry");
29
+ const relaunchFormEl = getRelaunchElement("form");
30
+ const retryButtonEl = getRelaunchElement("request");
31
+
32
+ // set a test cookie
33
+ const testCookie = "canSetCookies=true";
34
+ document.cookie = testCookie;
35
+ if (
36
+ document.cookie.split(";").some((cookie) => cookie.includes(testCookie))
37
+ ) {
38
+ // setting cookies works, continue login flow inline
39
+ // todo: remove test cookie
40
+ document.getElementById("redirect-form").submit();
41
+ return;
42
+ }
43
+
44
+ // open in new tab and restart login flow
45
+ show(successMessageEl);
46
+ openNewWindow();
47
+
48
+ if (newWindow) {
49
+ // tool opened successfully, POST login form data to opened window
50
+ relaunchFormEl.submit();
51
+ return;
52
+ }
53
+
54
+ // tool open blocked, tell user to allow popups and try again
55
+ show(retryMessageEl);
56
+ hide(successMessageEl);
57
+
58
+ retryButtonEl.addEventListener("click", () => {
59
+ // open tool and POST login data again
60
+ openNewWindow();
61
+ relaunchFormEl.submit();
62
+
63
+ show(successMessageEl);
64
+ hide(retryMessageEl);
65
+ });
66
+ });
@@ -0,0 +1,52 @@
1
+ .flex-container {
2
+ display: none;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans",
6
+ "Helvetica Neue", Arial, sans-serif;
7
+ font-size: 1.1em;
8
+ padding: 0 24px;
9
+ }
10
+
11
+ .flex-item {
12
+ margin-bottom: 10px;
13
+ text-align: center;
14
+ }
15
+
16
+ .flex-container div:first-child {
17
+ margin-top: 24px;
18
+ }
19
+
20
+ p {
21
+ margin: 0 0 0.5em 0;
22
+ }
23
+
24
+ button {
25
+ background: #008ee2;
26
+ color: #ffffff;
27
+ border: 1px solid;
28
+ border-color: #0079c1;
29
+ border-radius: 3px;
30
+ transition: background-color 0.2s ease-in-out;
31
+ display: inline-block;
32
+ position: relative;
33
+ padding: 8px 14px;
34
+ margin-bottom: 0;
35
+ font-size: 16px;
36
+ font-size: 1rem;
37
+ line-height: 20px;
38
+ text-align: center;
39
+ vertical-align: middle;
40
+ cursor: pointer;
41
+ text-decoration: none;
42
+ overflow: hidden;
43
+ text-shadow: none;
44
+ -webkit-user-select: none;
45
+ -moz-user-select: none;
46
+ user-select: none;
47
+ }
48
+
49
+ img {
50
+ width: 100px;
51
+ height: 100px;
52
+ }
@@ -0,0 +1,71 @@
1
+ module CanvasLtiThirdPartyCookies::RelaunchOnLogin
2
+ extend ActiveSupport::Concern
3
+
4
+ # this should replace your previous login render call, at the end of your login action.
5
+ #
6
+ # `redirect_url` (required): the authorization redirect URL to continue the login flow
7
+ #
8
+ # `redirect_data` (required): all form data required for the authorization redirect, which
9
+ # the previous login render call should have included in a form tag.
10
+ #
11
+ # `window_type`: (optional) Set to `:new_window` to open the tool in a
12
+ # new tab or window, or to `:popup` to open in a popup window.
13
+ # Defaults to `:new_window`.
14
+ #
15
+ # `width`: (optional) The width the popup window should be, in px. User
16
+ # has the discretion to ignore this. Only valid with window_type: popup.
17
+ # Defaults to 800px.
18
+ #
19
+ # `height`: (optional) The height the popup window should be, in px. User
20
+ # has the discretion to ignore this. Only valid with window_type: popup.
21
+ # Defaults to 600px.
22
+ #
23
+ # example:
24
+ # include CanvasLtiThirdPartyCookies::RelaunchOnLogin
25
+ # ...
26
+ # def login
27
+ # state, nonce = create_and_cache_state # handled elsewhere
28
+ # redirect_url = 'http://canvas.instructure.com/api/lti/authorize_redirect'
29
+ # redirect_data = {
30
+ # scope: 'openid',
31
+ # response_type: 'id_token',
32
+ # response_mode: 'form_post',
33
+ # prompt: 'none',
34
+ # redirect_uri: redirect_uri, # the launch url of the tool
35
+ # client_id: params.require(:client_id),
36
+ # login_hint: params.require(:login_hint),
37
+ # lti_message_hint: params.require(:lti_message_hint),
38
+ # state: state,
39
+ # nonce: nonce
40
+ # }
41
+ #
42
+ # relaunch_on_login(redirect_url, redirect_data)
43
+ # end
44
+ def relaunch_on_login(redirect_url, redirect_data, window_type: :new_window, width: 800, height: 600)
45
+ raise ArgumentError.new("window_type must be either :new_window or :popup") unless [:new_window, :popup].include? window_type
46
+
47
+ window_type_text = {
48
+ new_window: 'new tab',
49
+ popup: 'popup window'
50
+ }
51
+ form_target = 'login_relaunch'
52
+
53
+ render(
54
+ 'canvas_lti_third_party_cookies/relaunch_on_login',
55
+ locals: {
56
+ redirect_url: redirect_url,
57
+ redirect_data: redirect_data,
58
+ relaunch_url: request.url,
59
+ relaunch_data: params.permit(:canvas_region, :client_id, :iss, :login_hint, :lti_message_hint, :target_link_uri),
60
+ form_target: form_target,
61
+ window_type_text: window_type_text[window_type],
62
+ js_env: {
63
+ form_target: form_target,
64
+ window_type: window_type,
65
+ width: width,
66
+ height: height
67
+ }
68
+ }
69
+ )
70
+ end
71
+ end
@@ -0,0 +1,11 @@
1
+ <div id="<%= id %>" class="flex-container">
2
+ <div class="flex-item">
3
+ <img src="https://upload.wikimedia.org/wikipedia/commons/0/03/Oxygen480-apps-preferences-web-browser-cookies.svg" alt="Cookie" />
4
+ </div>
5
+
6
+ <div class="flex-item">
7
+ <strong>It looks like your browser doesn't like cookies.</strong>
8
+ </div>
9
+
10
+ <%= yield %>
11
+ </div>
@@ -0,0 +1,41 @@
1
+ <%= stylesheet_link_tag 'canvas_lti_third_party_cookies/relaunch_on_login' %>
2
+ <%= javascript_tag do %>
3
+ window.ENV = <%= js_env.to_json.html_safe %>
4
+ <% end %>
5
+ <%= javascript_include_tag 'canvas_lti_third_party_cookies/relaunch_on_login' %>
6
+
7
+ <%# when submitted, continue to login flow step 2: redirect back to Canvas for authentication %>
8
+ <%= form_tag(redirect_url, method: :post, id: 'redirect-form') do %>
9
+ <% redirect_data.each do |k, v| %>
10
+ <%= hidden_field_tag(k, v) %>
11
+ <% end %>
12
+ <% end %>
13
+
14
+ <%# when submitted, replay login flow step 1 in a new window %>
15
+ <%# the target attribute tells the form to POST in the window matching that target, which is opened below %>
16
+ <%= form_tag(relaunch_url, method: :post, id: 'relaunch-form', target: form_target) do %>
17
+ <% relaunch_data.each do |k, v| %>
18
+ <%= hidden_field_tag(k, v) %>
19
+ <% end %>
20
+ <% end %>
21
+
22
+ <%# default message to display on new window launch; hidden by default %>
23
+ <%= render 'canvas_lti_third_party_cookies/cookie_message', id: 'relaunch-success' do %>
24
+ <div class="flex-item">
25
+ <p>Some browsers block third-party cookies by default, which this app relies on for launch and sign-in features.</p>
26
+ <p>This app has opened in a <%= window_type_text %>, so that it can set its own cookies.
27
+ </div>
28
+ <% end %>
29
+
30
+ <%# message displayed when popups are blocked; hidden by default %>
31
+ <%= render 'canvas_lti_third_party_cookies/cookie_message', id: 'relaunch-retry' do %>
32
+ <div class="flex-item">
33
+ <p>Some browsers block third-party cookies by default, which this app relies on for launch and sign-in features.</p>
34
+ <p>Launching this app in a <%= window_type_text %>, separate from Canvas, will allow the app to set its own cookies.</p>
35
+ <p>To open the app, make sure your browser allows popups for this page and try again.</p>
36
+ </div>
37
+
38
+ <div class="flex-item">
39
+ <button id="relaunch-request">Open in <%= window_type_text.capitalize %></button>
40
+ </div>
41
+ <% end %>
@@ -1,5 +1,9 @@
1
1
  module CanvasLtiThirdPartyCookies
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace CanvasLtiThirdPartyCookies
4
+
5
+ initializer "CanvasLtiThirdPartyCookies.assets.precompile" do |app|
6
+ app.config.assets.precompile += %w( canvas_lti_third_party_cookies/relaunch_on_login.js canvas_lti_third_party_cookies/relaunch_on_login.css )
7
+ end
4
8
  end
5
9
  end
@@ -1,3 +1,3 @@
1
1
  module CanvasLtiThirdPartyCookies
2
- VERSION = '0.3.1'
2
+ VERSION = '1.0.0'
3
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.3.1
4
+ version: 1.0.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-03-03 00:00:00.000000000 Z
11
+ date: 2021-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 6.0.2
19
+ version: 6.0.3
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 6.0.2.1
22
+ version: 6.0.3.6
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: 6.0.2
29
+ version: 6.0.3
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 6.0.2.1
32
+ version: 6.0.3.6
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: browser
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -75,9 +75,11 @@ files:
75
75
  - LICENSE
76
76
  - README.md
77
77
  - Rakefile
78
- - app/controllers/concerns/canvas_lti_third_party_cookies/safari_launch.rb
79
- - app/views/canvas_lti_third_party_cookies/full_window_launch.erb
80
- - app/views/canvas_lti_third_party_cookies/request_storage_access.erb
78
+ - app/assets/javascripts/canvas_lti_third_party_cookies/relaunch_on_login.js
79
+ - app/assets/stylesheets/canvas_lti_third_party_cookies/relaunch_on_login.css
80
+ - app/controllers/concerns/canvas_lti_third_party_cookies/relaunch_on_login.rb
81
+ - app/views/canvas_lti_third_party_cookies/_cookie_message.erb
82
+ - app/views/canvas_lti_third_party_cookies/relaunch_on_login.erb
81
83
  - app/views/layouts/application.html.erb
82
84
  - lib/canvas_lti_third_party_cookies.rb
83
85
  - lib/canvas_lti_third_party_cookies/engine.rb
@@ -1,71 +0,0 @@
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
- # 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 CanvasLtiThirdPartyCookies::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. Note that this is not an actual LTI launch, but
27
- # just opaquely passing on the data from launch #3.
28
- return if params[:storage_access_status].present?
29
-
30
- # Safari launch #2: Full-window launch, solely for first-party user interaction.
31
- # During a full-window launch, Canvas provides a :platform_redirect_url that
32
- # will launch the tool again within an iframe in Canvas. (#3)
33
- if params[:platform_redirect_url].present?
34
- return render(
35
- 'canvas_lti_third_party_cookies/full_window_launch',
36
- locals: { platform_redirect_url: params[:platform_redirect_url] }
37
- )
38
- end
39
-
40
- # Safari launch #1: request Storage Access, then relaunch the tool. (#4)
41
- # If request fails, request a full window launch instead. (#2)
42
- # Safari launch #3: Relaunched by Canvas after full-window launch,
43
- # request Storage Access and then relaunch the tool. (#4)
44
- # Pass along any parameters provided by the tool that are needed to launch correctly,
45
- # and tell the tool that it has Storage Access.
46
- render(
47
- 'canvas_lti_third_party_cookies/request_storage_access',
48
- locals: {
49
- launch_url: launch_url,
50
- relaunch_url: relaunch_url(launch_url, launch_params),
51
- launch_data: launch_data.merge({ storage_access_status: "granted"})
52
- }
53
- )
54
- end
55
-
56
- # Safari launch #4 (described above) is actually an internal opaque redirect of launch #3
57
- # and not a real Canvas LTI launch, so the id_token (and specifically the nonce inside)
58
- # is exactly the same. Normally, ignoring the nonce is a Bad Idea since it can allow
59
- # replay attacks, but for this specific situation (the request is an internal redirect)
60
- # it's a sufficient hack.
61
- def should_ignore_nonce?
62
- params[:storage_access_status] == "granted" && URI.parse(request.referer).host == request.host
63
- end
64
-
65
- private
66
-
67
- def relaunch_url(launch_url, launch_params)
68
- return launch_url if launch_params.empty?
69
- "#{launch_url}?#{launch_params.to_query}"
70
- end
71
- end
@@ -1,80 +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
- <p>In some cases, you may need to relaunch this app yourself.</p>
74
- </div>
75
-
76
- <div class="flex-item">
77
- <button id="redirect">Relaunch App in Canvas</button>
78
- </div>
79
- <div>
80
- </div>
@@ -1,115 +0,0 @@
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
- <p>A dialog may also appear asking you to navigate somewhere else.</p>
103
- <p>If it does, save your work first and then click Leave Page.</p>
104
- </div>
105
-
106
- <div class="flex-item">
107
- <button id="request">Continue to App</button>
108
- </div>
109
- <div>
110
- </div>
111
- <form id="relaunch" method="POST" action="<%= relaunch_url %>">
112
- <% launch_data.each do |key, value| -%>
113
- <%= hidden_field_tag key, value %>
114
- <% end -%>
115
- </form>