ruby_native 0.14.2 → 0.15.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 +4 -4
- data/lib/generators/ruby_native/templates/ruby_native.yml +3 -0
- data/lib/ruby_native/helper.rb +7 -0
- data/lib/ruby_native/oauth_middleware.rb +26 -25
- data/lib/ruby_native/set_cookie_header.rb +26 -0
- data/lib/ruby_native/tunnel_cookie_middleware.rb +3 -4
- data/lib/ruby_native/version.rb +1 -1
- data/lib/ruby_native.rb +15 -9
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e8d87be28530140900677b188eb358c7a1dedaa5966760494cd714d58f988017
|
|
4
|
+
data.tar.gz: 6b2842f30c59244680a0b6a853d9c43617c33dda0cb1232089159fdc06fbf757
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 79c71e7a97da93357aa8e927a41dbdd5742919dcfd9bdfddc7b2d0e215c37489bacfd6b3cdf77b2b1b86e13f0a081777830c732d0ebbacc52398a8026609ff05
|
|
7
|
+
data.tar.gz: 1d5946a4083783f961363b05e61f3e3c62909d60c90108cc4e67594149a559fa62810507dd4720ea67fb9cb7e6c2a8c0e7198ed0593b0d7fdea140afc1145476
|
|
@@ -69,6 +69,9 @@ appearance:
|
|
|
69
69
|
# foreground_color: "#FFFFFF"
|
|
70
70
|
# tab_bar:
|
|
71
71
|
# background_color: "#1E293B"
|
|
72
|
+
# # Icon and label color. Defaults to the theme's, with tint_color on the
|
|
73
|
+
# # selected tab. Set this and unselected tabs dim to 60% of it.
|
|
74
|
+
# foreground_color: "#FFFFFF"
|
|
72
75
|
|
|
73
76
|
# Show a launch splash while the first screen loads. It holds your launch icon
|
|
74
77
|
# and background_color in frame, with a spinner, until the web content paints.
|
data/lib/ruby_native/helper.rb
CHANGED
|
@@ -230,6 +230,13 @@ module RubyNative
|
|
|
230
230
|
tag.div(data: { native_overscroll_top: top, native_overscroll_bottom: bottom || top }, hidden: true)
|
|
231
231
|
end
|
|
232
232
|
|
|
233
|
+
# Hides the iOS keyboard toolbar (the previous/next/Done bar) on this page.
|
|
234
|
+
# Page-level: the toolbar belongs to the web view, not to individual fields.
|
|
235
|
+
def native_keyboard_tag(toolbar: false)
|
|
236
|
+
return "".html_safe if toolbar
|
|
237
|
+
tag.div(data: { native_keyboard_toolbar: false }, hidden: true)
|
|
238
|
+
end
|
|
239
|
+
|
|
233
240
|
def native_haptic_data(feedback = :success, **data)
|
|
234
241
|
feedback = feedback.to_s
|
|
235
242
|
feedback = "success" if feedback.empty?
|
|
@@ -18,7 +18,7 @@ module RubyNative
|
|
|
18
18
|
request = ActionDispatch::Request.new(env)
|
|
19
19
|
on_oauth_path = oauth_path?(request)
|
|
20
20
|
started_native_oauth = on_oauth_path && request.params["ruby_native"] == "1"
|
|
21
|
-
|
|
21
|
+
param_scheme = permitted_scheme(request.params["callback_scheme"]) if started_native_oauth
|
|
22
22
|
|
|
23
23
|
status, headers, body = @app.call(env)
|
|
24
24
|
|
|
@@ -26,27 +26,27 @@ module RubyNative
|
|
|
26
26
|
relax_cookie_samesite!(headers)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
if started_native_oauth &&
|
|
29
|
+
if started_native_oauth && param_scheme.present? && redirect?(status)
|
|
30
30
|
Rails.logger.debug { "[RubyNative] OAuth started for #{request.path}, setting tracking cookie" }
|
|
31
|
-
set_cookie(headers,
|
|
31
|
+
set_cookie(headers, param_scheme)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
# On a start request the param names the app asking right now, so a stale
|
|
35
|
+
# cookie from another app on the same domain must never win.
|
|
36
|
+
callback_scheme = started_native_oauth ? param_scheme : permitted_scheme(read_cookie(request))
|
|
35
37
|
|
|
36
|
-
if
|
|
38
|
+
if callback_scheme && redirect?(status)
|
|
37
39
|
location = headers["location"] || headers["Location"]
|
|
38
40
|
|
|
39
41
|
if auth_failure?(location)
|
|
40
42
|
Rails.logger.info { "[RubyNative] OAuth failed, redirecting to native app" }
|
|
41
|
-
|
|
42
|
-
return redirect_to_native(stored_scheme, error: true)
|
|
43
|
+
return redirect_to_native(callback_scheme, error: true)
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
if internal_redirect?(request, location)
|
|
46
47
|
token = build_token(headers, location)
|
|
47
48
|
Rails.logger.info { "[RubyNative] OAuth succeeded, redirecting to native app" }
|
|
48
|
-
|
|
49
|
-
return redirect_to_native(stored_scheme, token: token)
|
|
49
|
+
return redirect_to_native(callback_scheme, token: token)
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
@@ -116,8 +116,14 @@ module RubyNative
|
|
|
116
116
|
nil
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
# Attributes must match set_cookie or browsers keep the original cookie.
|
|
120
|
+
def expire_cookie(headers)
|
|
121
|
+
headers["set-cookie"] = Rack::Utils.delete_set_cookie_header(COOKIE_NAME, {
|
|
122
|
+
path: "/",
|
|
123
|
+
httponly: true,
|
|
124
|
+
secure: true,
|
|
125
|
+
same_site: :none
|
|
126
|
+
})
|
|
121
127
|
end
|
|
122
128
|
|
|
123
129
|
def verifier
|
|
@@ -133,16 +139,12 @@ module RubyNative
|
|
|
133
139
|
# breaks OmniAuth's state verification. Relax existing cookies
|
|
134
140
|
# to SameSite=None so the session cookie survives Apple's callback.
|
|
135
141
|
def relax_cookie_samesite!(headers)
|
|
136
|
-
|
|
137
|
-
return unless raw
|
|
138
|
-
|
|
139
|
-
cookies = raw.is_a?(Array) ? raw : raw.split("\n")
|
|
140
|
-
headers["set-cookie"] = cookies.map { |cookie|
|
|
142
|
+
SetCookieHeader.rewrite!(headers) do |cookie|
|
|
141
143
|
next cookie unless cookie.match?(/SameSite=Lax/i)
|
|
142
144
|
cookie.gsub(/SameSite=Lax/i, "SameSite=None").then { |c|
|
|
143
145
|
c.include?("Secure") ? c : "#{c}; Secure"
|
|
144
146
|
}
|
|
145
|
-
|
|
147
|
+
end
|
|
146
148
|
end
|
|
147
149
|
|
|
148
150
|
def redirect?(status)
|
|
@@ -165,23 +167,22 @@ module RubyNative
|
|
|
165
167
|
end
|
|
166
168
|
|
|
167
169
|
def build_token(headers, redirect_url)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
when Array then raw_cookies
|
|
172
|
-
else []
|
|
173
|
-
end
|
|
170
|
+
# Restoring the tracking cookie into the app's cookie jar would hijack the
|
|
171
|
+
# next in-app redirect, so it never rides along in the token.
|
|
172
|
+
cookies = SetCookieHeader.read(headers).reject { |c| c.start_with?("#{COOKIE_NAME}=") }
|
|
174
173
|
|
|
175
174
|
redirect_url = "/" if auth_start_path?(redirect_url)
|
|
176
175
|
|
|
177
|
-
Rails.logger.info { "[RubyNative] Captured #{cookies.size} cookies for token
|
|
176
|
+
Rails.logger.info { "[RubyNative] Captured #{cookies.size} cookies for token" }
|
|
178
177
|
|
|
179
178
|
self.class.build_token(cookies: cookies, redirect_url: redirect_url)
|
|
180
179
|
end
|
|
181
180
|
|
|
182
181
|
def redirect_to_native(callback_scheme, token: nil, error: false)
|
|
183
182
|
query = error ? "error=true" : "token=#{CGI.escape(token)}"
|
|
184
|
-
|
|
183
|
+
headers = {"location" => "#{callback_scheme}://auth/callback?#{query}"}
|
|
184
|
+
expire_cookie(headers)
|
|
185
|
+
[302, headers, [""]]
|
|
185
186
|
end
|
|
186
187
|
|
|
187
188
|
def auth_start_path?(url)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module RubyNative
|
|
2
|
+
# Rack 2 carries Set-Cookie as one newline joined String, Rack 3 as an Array
|
|
3
|
+
# of cookie strings. Rewrites must hand back whichever shape came in.
|
|
4
|
+
module SetCookieHeader
|
|
5
|
+
KEYS = ["set-cookie", "Set-Cookie"].freeze
|
|
6
|
+
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def read(headers)
|
|
10
|
+
raw = KEYS.map { |key| headers[key] }.compact.first
|
|
11
|
+
case raw
|
|
12
|
+
when Array then raw
|
|
13
|
+
when String then raw.split("\n")
|
|
14
|
+
else []
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def rewrite!(headers, &block)
|
|
19
|
+
key = KEYS.find { |k| headers[k] }
|
|
20
|
+
return unless key
|
|
21
|
+
|
|
22
|
+
rewritten = read(headers).map(&block)
|
|
23
|
+
headers[key] = headers[key].is_a?(Array) ? rewritten : rewritten.join("\n")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -34,10 +34,9 @@ module RubyNative
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def strip_cookie_domain!(headers)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
headers["set-cookie"] = (stripped.length == 1) ? stripped.first : stripped
|
|
37
|
+
SetCookieHeader.rewrite!(headers) do |cookie|
|
|
38
|
+
cookie.gsub(/;\s*domain=[^;]*/i, "")
|
|
39
|
+
end
|
|
41
40
|
end
|
|
42
41
|
end
|
|
43
42
|
end
|
data/lib/ruby_native/version.rb
CHANGED
data/lib/ruby_native.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "ruby_native/helper"
|
|
|
4
4
|
require "ruby_native/native_version"
|
|
5
5
|
require "ruby_native/native_detection"
|
|
6
6
|
require "ruby_native/inertia_support"
|
|
7
|
+
require "ruby_native/set_cookie_header"
|
|
7
8
|
require "ruby_native/oauth_middleware"
|
|
8
9
|
require "ruby_native/tunnel_cookie_middleware"
|
|
9
10
|
require "ruby_native/iap/event"
|
|
@@ -181,17 +182,22 @@ module RubyNative
|
|
|
181
182
|
# "callback" and send sign-in into a loop. The callback round-trip is handled
|
|
182
183
|
# automatically by OAuthMiddleware's tracking cookie, so it never needs
|
|
183
184
|
# listing. Drop any entry that is the "/callback" child of another listed
|
|
184
|
-
# path and warn, so a copied-in callback can't break native sign-in.
|
|
185
|
+
# path and warn, so a copied-in callback can't break native sign-in. The value
|
|
186
|
+
# is always written back as an array: a scalar in YAML fails the whole native
|
|
187
|
+
# config decode on both platforms.
|
|
185
188
|
def self.normalize_oauth_paths
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return if callbacks.empty?
|
|
189
|
+
auth = self.config[:auth]
|
|
190
|
+
return unless auth.is_a?(Hash) && auth.key?(:oauth_paths)
|
|
189
191
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
paths = Array(auth[:oauth_paths])
|
|
193
|
+
callbacks = paths.select { |path| paths.any? { |start| path == "#{start}/callback" } }
|
|
194
|
+
if callbacks.any?
|
|
195
|
+
Rails.logger.warn(
|
|
196
|
+
"[RubyNative] Ignoring OAuth callback path(s) in config/ruby_native.yml " \
|
|
197
|
+
"(#{callbacks.join(", ")}). List only the authorize path; callbacks are handled automatically."
|
|
198
|
+
)
|
|
199
|
+
end
|
|
200
|
+
auth[:oauth_paths] = paths - callbacks
|
|
195
201
|
end
|
|
196
202
|
|
|
197
203
|
# Entries are path prefixes: "/pair/" links every URL under it. A leading
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_native
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.15.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joe Masilotti
|
|
@@ -109,6 +109,7 @@ files:
|
|
|
109
109
|
- lib/ruby_native/native_version.rb
|
|
110
110
|
- lib/ruby_native/oauth_middleware.rb
|
|
111
111
|
- lib/ruby_native/screenshots/sign_in_helper.rb
|
|
112
|
+
- lib/ruby_native/set_cookie_header.rb
|
|
112
113
|
- lib/ruby_native/tunnel_cookie_middleware.rb
|
|
113
114
|
- lib/ruby_native/version.rb
|
|
114
115
|
homepage: https://rubynative.com
|