atomic_lti 1.7.0 → 1.8.1

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: 265fc192cec38bb2a8dbac1f8fc4eaf3388f9240de43f5558ea28f52c3272047
4
- data.tar.gz: cd5cbbb67fc5f2bed2cf959421321aae77a0a8a73504c6ff3a1729d7e78bc8e2
3
+ metadata.gz: c5a9ab9e61066b4cf5a463e83db17a6ffbded2fce44251b5506dbfb7a385d1df
4
+ data.tar.gz: 1922245e4850a6f2baa0cf7cd44a6ae05c3d2937ee957c8ddca46299fa10d302
5
5
  SHA512:
6
- metadata.gz: 8b5c6de47055ebad0a7dfb6a8a8bfeb3f5fdf4598799bd0ed6805ae6c665282bf37130a6a8e37c31cacfdfb77bc10fd8971e85592e7eaca5ae9539015decfd05
7
- data.tar.gz: 775b35f941fd47e529c68906fe47541b3c2736c0c4659179485e5e3da2492edcae02bd537d9bcd4cd621a5f99539f3b466d8f91e0584fa0f14b5fa5c381df9f9
6
+ metadata.gz: 8e1506ff8b48c8b65164be73cf73408a102af3235f6b1a241068ddca74c57bf8b5c7241e7f5308bf3152a2e6e3bb5288afc7c32aa36d2ef95f896e5ed62bf4ed
7
+ data.tar.gz: 6a114eefe55c0337690997eaa274685eb3025956a1ac82927c595cd6d716436a1ac2384a2155d568eece1fa002a96e0faa4c3226c9f0c6eb64bcb1328bdeaca7
@@ -119,6 +119,7 @@ module AtomicLti
119
119
  def self.request_token_uncached(iss:, deployment_id:, scopes:)
120
120
  # Details here:
121
121
  # https://www.imsglobal.org/spec/security/v1p0/#using-json-web-tokens-with-oauth-2-0-client-credentials-grant
122
+ puts "GETTING TOKEN"
122
123
  body = {
123
124
  grant_type: "client_credentials",
124
125
  client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
@@ -83,7 +83,9 @@ module AtomicLti
83
83
 
84
84
  # Validate that we are at the target_link_uri
85
85
  target_link_uri = decoded_token[AtomicLti::Definitions::TARGET_LINK_URI_CLAIM]
86
- if validate_target_link_url && target_link_uri != requested_target_link_uri
86
+
87
+ if validate_target_link_url &&
88
+ !matching_uri?(target_link_uri, requested_target_link_uri, ignore_host: AtomicLti.update_target_link_host)
87
89
  errors.push(
88
90
  "LTI token target link uri '#{target_link_uri}' doesn't match url '#{requested_target_link_uri}'",
89
91
  )
@@ -123,5 +125,16 @@ module AtomicLti
123
125
  decoded_token["aud"]
124
126
  end
125
127
  end
128
+
129
+ def self.matching_uri?(target, actual, ignore_host:)
130
+ t = URI.parse(target)
131
+ a = URI.parse(actual)
132
+
133
+ t.scheme == a.scheme &&
134
+ t.path == a.path &&
135
+ t.query == a.query &&
136
+ t.fragment == a.fragment &&
137
+ (ignore_host || t.host == a.host)
138
+ end
126
139
  end
127
140
  end
@@ -2,8 +2,9 @@ module AtomicLti
2
2
  module Services
3
3
  class NamesAndRoles < AtomicLti::Services::Base
4
4
 
5
- def initialize(id_token_decoded:)
6
- super(id_token_decoded: id_token_decoded)
5
+ def initialize(id_token_decoded: nil, iss: nil, deployment_id: nil, context_memberships_url: nil)
6
+ @context_memberships_url = context_memberships_url || id_token_decoded.dig(AtomicLti::Definitions::NAMES_AND_ROLES_CLAIM, "context_memberships_url")
7
+ super(id_token_decoded:, iss:, deployment_id:)
7
8
  end
8
9
 
9
10
  def scopes
@@ -1,6 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
  <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
4
5
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
5
6
  <%= stylesheet_link_tag "atomic_lti/launch" %>
6
7
  </head>
@@ -13,7 +14,7 @@
13
14
  </p>
14
15
  </div>
15
16
  </noscript>
16
- <form action="<%= @launch_url -%>" method="POST">
17
+ <form action="<%= @launch_url -%>" method="POST" accept-charset="UTF-8">
17
18
  <% @launch_params.each do |name, value| -%>
18
19
  <%= hidden_field_tag(name, value) %>
19
20
  <% end -%>
@@ -31,13 +31,17 @@ module AtomicLti
31
31
  headers = { "Content-Type" => "text/html" }
32
32
  Rack::Utils.set_cookie_header!(
33
33
  headers, "#{OPEN_ID_COOKIE_PREFIX}storage",
34
- { value: "1", path: "/", max_age: 365.days, http_only: false, secure: true, same_site: "None" }
34
+ { value: "1", path: "/", max_age: 365.days, http_only: false, secure: true, same_site: "None", partitioned: true }
35
35
  )
36
36
  Rack::Utils.set_cookie_header!(
37
37
  headers, "#{OPEN_ID_COOKIE_PREFIX}#{state}",
38
- { value: 1, path: "/", max_age: 1.minute, http_only: false, secure: true, same_site: "None" }
38
+ { value: 1, path: "/", max_age: 1.minute, http_only: false, secure: true, same_site: "None", partitioned: true }
39
39
  )
40
40
 
41
+ # Ensure our cookies are partitioned. This can be removed once our Rack version
42
+ # understands the partitioned: argument above.
43
+ headers[Rack::SET_COOKIE] = partition_cookies(headers[Rack::SET_COOKIE])
44
+
41
45
  redirect_uri = [request.base_url, AtomicLti.oidc_redirect_path].join
42
46
  response_url = build_oidc_response(request, state, nonce, redirect_uri)
43
47
 
@@ -111,6 +115,13 @@ module AtomicLti
111
115
  target_link_uri = id_token_decoded[AtomicLti::Definitions::TARGET_LINK_URI_CLAIM] ||
112
116
  File.join("#{uri.scheme}://#{uri.host}", AtomicLti.default_deep_link_path)
113
117
 
118
+ target = URI.parse(target_link_uri)
119
+
120
+ # Optionally update the target link host to match the redirect host
121
+ if AtomicLti.update_target_link_host && target.host != uri.host
122
+ target.host = uri.host
123
+ end
124
+
114
125
  # We want to strip out the redirect path params from the request params
115
126
  # so that we can support having the redirect path be the same as the
116
127
  # launch path, only differentiated by a query parameter. This is needed
@@ -131,7 +142,7 @@ module AtomicLti
131
142
  template: "atomic_lti/shared/redirect",
132
143
  assigns: {
133
144
  launch_params: launch_params,
134
- launch_url: target_link_uri,
145
+ launch_url: target,
135
146
  },
136
147
  )
137
148
 
@@ -363,5 +374,30 @@ module AtomicLti
363
374
  platformOIDCUrl: platform.oidc_url,
364
375
  }.compact
365
376
  end
377
+
378
+ def partition_cookies(header)
379
+ # Some versions of rack add multiple cookies as a newline-separated string, and others
380
+ # as an array of strings.
381
+ case header
382
+ when String
383
+ header.split("\n").map do |cookie|
384
+ if !cookie.match? /partitioned/i
385
+ "#{cookie}; partitioned"
386
+ else
387
+ cookie
388
+ end
389
+ end.join("\n")
390
+ when Array
391
+ header.map do |cookie|
392
+ if !cookie.match? /partitioned/i
393
+ "#{cookie}; partitioned"
394
+ else
395
+ cookie
396
+ end
397
+ end
398
+ else
399
+ header
400
+ end
401
+ end
366
402
  end
367
403
  end
@@ -1,3 +1,3 @@
1
1
  module AtomicLti
2
- VERSION = "1.7.0".freeze
2
+ VERSION = "1.8.1".freeze
3
3
  end
data/lib/atomic_lti.rb CHANGED
@@ -32,6 +32,12 @@ module AtomicLti
32
32
  # requires this, but Canvas doesn't currently support it.
33
33
  mattr_accessor :set_post_message_origin, default: false
34
34
 
35
+ # Set to true to update the target link uri host to match the oidc redirect host.
36
+ # Enable this when a single LTI install needs to support launches across multiple hosts.
37
+ # Setting this avoids state validation problems on launch since state cookies and
38
+ # postMessage storage won't work across different hosts.
39
+ mattr_accessor :update_target_link_host, default: false
40
+
35
41
  mattr_accessor :privacy_policy_url, default: "#"
36
42
  mattr_accessor :privacy_policy_message, default: nil
37
43
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atomic_lti
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Petro
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-12-26 00:00:00.000000000 Z
13
+ date: 2024-03-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: pg
@@ -133,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
133
  - !ruby/object:Gem::Version
134
134
  version: '0'
135
135
  requirements: []
136
- rubygems_version: 3.4.19
136
+ rubygems_version: 3.4.10
137
137
  signing_key:
138
138
  specification_version: 4
139
139
  summary: AtomicLti implements the LTI Advantage specification.