canvas_lti_third_party_cookies 0.4.0 → 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: 8d232d4448ee62b75121b20f70143f70f75e0ea38a83408b3ed42bb51ce41247
4
- data.tar.gz: 44605463e21036992d5e0dadc1ac7180384e3f4186917e0139fe586c9451c936
3
+ metadata.gz: bfadd9bf819df5cfb7740b3b1c88f7436337b0bafbc243d9df216af7e948cd9e
4
+ data.tar.gz: 6a539c02d809cbffbc9693a69567942af8ba24d839299d0baca93e3ebe1ef676
5
5
  SHA512:
6
- metadata.gz: 65a832d626b6062a7f4982417681a267bab2d49a6d070e4aef5e99b28d947fdb9d22ba73fc51badc1ceb4a9a7b59e45b3f08f8998fb1c2a286d6ec3bebd440f9
7
- data.tar.gz: 47b3da8ab13ac2b3b9e9fae11ccaba87344b2cce53d98edbe6f96250c37c54e98210b6d0345180adbd57e1990516c89f6eed8a6a9304a8abcd2e9ae44a9fe4dc
6
+ metadata.gz: a346aec85ae07519abe21496dbec0126bab0ab6ffc96de0d0b7c23be9eea02080cde3b7949295b598e9827c597442d83f866c758f0790a5bfad4da4faa4821cb
7
+ data.tar.gz: 9fb2fbc396790aca7e388e430adcd36ff2b2acf5d3f94309413e732b1ad354fdac8ee76197f1b3e74948ac59731cc10cbd3c931968361d0d6d3176c0e4af8a73
data/README.md CHANGED
@@ -1,10 +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 unfortunately doesn't completely work for LTI use cases.
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
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
- 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
7
- does here: https://github.com/instructure/canvas-lms/blob/master/public/javascripts/lti/post_message/requestFullWindowLaunch.js
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.
8
7
 
9
8
  ## Installation
10
9
  Add this line to your application's Gemfile:
@@ -24,18 +23,41 @@ $ bundle install
24
23
  Choose the Rails controller action that's used to launch your tool and set cookies. Set the before_action callback
25
24
  below to run on that action, and pass the data needed.
26
25
 
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
+ * `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.
31
38
 
39
+ example:
32
40
  ```ruby
33
- include CanvasLtiThirdPartyCookies::SafariLaunch
34
- #...
35
- before_action -> {
36
- placement = decoded_id_token['https://www.instructure.com/placement']
37
- handle_safari_launch(placement: placement, window_type: :popup)
38
- }
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
39
61
  ```
40
62
 
41
63
  ## Testing
@@ -47,7 +69,7 @@ $ rails test
47
69
  ## Publishing New Versions
48
70
 
49
71
  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`
72
+ 2. `rake install`
73
+ 3. Commit, push, and merge that change.
52
74
  4. `gem push pkg/canvas_lti_third_party_cookies-<version>.gem`
53
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.4.0'
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.4.0
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-26 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,8 +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/prep_for_full_window_launch.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
80
83
  - app/views/layouts/application.html.erb
81
84
  - lib/canvas_lti_third_party_cookies.rb
82
85
  - lib/canvas_lti_third_party_cookies/engine.rb
@@ -1,60 +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
- #
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,99 +0,0 @@
1
- <%= javascript_tag do -%>
2
- const requestFullWindowLaunch = () => {
3
- console.log("clicked")
4
- window.parent.postMessage(
5
- {
6
- messageType: "requestFullWindowLaunch",
7
- data: {
8
- url: "<%= launch_url %>",
9
- placement: "<%= placement %>",
10
- launchType: "<%= window_type %>",
11
- launchOptions: {
12
- width: <%= width %>,
13
- height: <%= height %>
14
- }
15
- }
16
- },
17
- "*"
18
- );
19
- };
20
- document.addEventListener("DOMContentLoaded", () => {
21
- console.log("loaded")
22
- document.getElementById("request").addEventListener("click", requestFullWindowLaunch)
23
- });
24
- <% end %>
25
- <style type="text/css">
26
- .flex-container {
27
- display: flex;
28
- flex-direction: column;
29
- height: 100%;
30
- font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
31
- font-size: 1.1em;
32
- padding: 0 24px;
33
- }
34
-
35
- .flex-item {
36
- margin-bottom: 10px;
37
- text-align: center;
38
- }
39
-
40
- .flex-container div:first-child {
41
- margin-top: 24px;
42
- }
43
-
44
- p {
45
- margin: 0 0 0.5em 0;
46
- }
47
-
48
- button {
49
- background: #008EE2;
50
- color: #ffffff;
51
- border: 1px solid;
52
- border-color: #0079C1;
53
- border-radius: 3px;
54
- transition: background-color 0.2s ease-in-out;
55
- display: inline-block;
56
- position: relative;
57
- padding: 8px 14px;
58
- margin-bottom: 0;
59
- font-size: 16px;
60
- font-size: 1rem;
61
- line-height: 20px;
62
- text-align: center;
63
- vertical-align: middle;
64
- cursor: pointer;
65
- text-decoration: none;
66
- overflow: hidden;
67
- text-shadow: none;
68
- -webkit-user-select: none;
69
- -moz-user-select: none;
70
- }
71
-
72
- #safari-logo {
73
- width: 100px;
74
- height: 100px;
75
- }
76
- </style>
77
-
78
- <div class="flex-container">
79
- <div class="flex-item">
80
- <img id="safari-logo" src="https://upload.wikimedia.org/wikipedia/commons/5/52/Safari_browser_logo.svg" alt="Safari Logo" />
81
- </div>
82
-
83
- <div class="flex-item">
84
- <strong>It looks like you are using Safari.</strong>
85
- </div>
86
-
87
- <div class="flex-item">
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%>
93
- </div>
94
-
95
- <div class="flex-item">
96
- <button id="request">Open <%if window_type == "popup"%>Popup<%else%>in New Tab<%end%></button>
97
- </div>
98
- <div>
99
- </div>