git-fit 0.10.9 → 0.10.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 990b506f035333d622b5112652251ecfb100fe152493439b2573fe002dcf2420
4
- data.tar.gz: 8935f8b3f86d62926666ff6fbd37401b230f4182260421c40b7a2e5ecc9e0d03
3
+ metadata.gz: c4cd7d43e318f96dcab83874635c24e7ead0ec772f550f2fdcd9ea7c81a6a44b
4
+ data.tar.gz: e106683db19d232870990bcbc59bcb35f59be4fec8c33d1141ec858632446522
5
5
  SHA512:
6
- metadata.gz: 8c5306f042f21e18a8f5761342fd70a8ae13a5660fc03e02496b8dd0288e87d8711cb4bf826fa33d3509ac5497da70730708f40bb24d8cf13157ba93e94f52e6
7
- data.tar.gz: d5cabbcad852447fc7b0c50489de64f83d8c732f849f0c483704f6565244759b841864d09c72bba840cb2cc265192c1b1f3b364b01c306539dd55d31fc3977e9
6
+ metadata.gz: da0ac336bbe16839be618bb0ddbcc6db7513630231b89cd690249568908d0fbe0f20110ec2904d589e18b3bb2ff14431e7138b846b9c3dd0ed65b4018f033e29
7
+ data.tar.gz: 101b8bc30d222858e2b61a592027ae701fb4563fdfeb832688929ba01cd7c87588411023cec5057b8af8fe7f81a15c18de62a3b25a2b97577a3f11496de3b672
@@ -36,6 +36,7 @@ module GitFit
36
36
  MOBILE_FINGERPRINT = 'safari18_0'
37
37
  TLS_FINGERPRINTS = %w[safari18_0 safari17_0 chrome120 edge101 chrome124].freeze
38
38
  CURL_BINARY = 'curl-impersonate'
39
+ AUTH_FLAG = '-A'
39
40
 
40
41
  module_function
41
42
 
@@ -129,7 +130,7 @@ module GitFit
129
130
  if resp_type == 'MFA_REQUIRED'
130
131
  method = res.dig('customerMfaInfo', 'mfaLastMethodUsed') || 'email'
131
132
  res = handle_mfa(sso, method, login_params, post_headers, mfa_code,
132
- impersonate: MOBILE_FINGERPRINT, jar: jar, output: body)
133
+ impersonate: MOBILE_FINGERPRINT, jar: jar, output: body, email: email)
133
134
  resp_type = res.dig('responseStatus', 'type')
134
135
  end
135
136
 
@@ -215,7 +216,7 @@ module GitFit
215
216
  if resp_type == 'MFA_REQUIRED'
216
217
  method = res.dig('customerMfaInfo', 'mfaLastMethodUsed') || 'email'
217
218
  res = handle_mfa(sso, method, login_params, post_headers, mfa_code,
218
- impersonate: imp, jar: jar, output: body)
219
+ impersonate: imp, jar: jar, output: body, email: email)
219
220
  resp_type = res.dig('responseStatus', 'type')
220
221
  end
221
222
 
@@ -230,8 +231,8 @@ module GitFit
230
231
  nil
231
232
  end
232
233
 
233
- def handle_mfa(sso, method, login_params, post_headers, mfa_code, impersonate:, jar:, output:)
234
- code = resolve_mfa_code(mfa_code)
234
+ def handle_mfa(sso, method, login_params, post_headers, mfa_code, impersonate:, jar:, output:, email:)
235
+ code = resolve_mfa_code(mfa_code, email)
235
236
  mfa_data = {
236
237
  'mfaMethod' => method,
237
238
  'mfaVerificationCode' => code,
@@ -266,22 +267,22 @@ module GitFit
266
267
  raise GitFit::Sync::AuthError, 'MFA verification failed on all endpoints'
267
268
  end
268
269
 
269
- def resolve_mfa_code(mfa_code)
270
- code = mfa_code
271
- code = ENV['SYNC__GARMIN__MFA_CODE'] if code.nil? || code.to_s.empty?
272
- if (code.nil? || code.to_s.empty?) && $stdin.tty?
273
- $stderr.write('Enter MFA code: ')
274
- $stderr.flush
275
- code = $stdin.gets
270
+ def resolve_mfa_code(mfa_code, email)
271
+ code = mfa_code.to_s.strip
272
+ if code.empty?
273
+ warn "[garmin_auth] Garmin sent a verification code to #{email}"
274
+ $stderr.write('Enter the 6-digit code: ') if $stdin.tty?
275
+ code = $stdin.gets.to_s.strip
276
276
  end
277
- if code.nil? || code.to_s.empty?
278
- raise GitFit::Sync::AuthError,
279
- 'MFA required but SYNC__GARMIN__MFA_CODE env var not set and stdin unavailable'
280
- end
281
- code = code.strip
282
- raise GitFit::Sync::AuthError, 'SYNC__GARMIN__MFA_CODE is empty' if code.empty?
283
-
284
- code
277
+ return code unless code.empty?
278
+
279
+ raise GitFit::Sync::AuthError,
280
+ "MFA required — check your email (#{email}) for the code.\n" \
281
+ " This code is valid for a few minutes.\n\n" \
282
+ " To continue, choose one:\n" \
283
+ " echo \"<CODE>\" | git fit auth garmin #{AUTH_FLAG}\n" \
284
+ " git fit auth garmin #{AUTH_FLAG} --mfa-code <CODE>\n\n" \
285
+ " If the code has expired, request a new one: git fit auth garmin #{AUTH_FLAG}"
285
286
  end
286
287
 
287
288
  def check_curl_available!
@@ -19,12 +19,12 @@ module GitFit
19
19
  LOGIN_DELAY_MAX = 45.0
20
20
  SESSION_CACHE_DIR = 'data/cache'
21
21
  REDIRECT_STATUSES = [301, 302, 303, 307, 308].freeze
22
+ AUTH_FLAG = '-B'
22
23
 
23
24
  module_function
24
25
 
25
26
  def call(email:, password:, domain: 'garmin.com', mfa_code: nil)
26
27
  mfa_code = mfa_code.to_s.strip
27
- mfa_code = ENV['SYNC__GARMIN__MFA_CODE'].to_s.strip if mfa_code.empty?
28
28
  sso = "https://sso.#{domain}"
29
29
  session_path = _session_file_path(domain)
30
30
  chrome_path = _find_chrome
@@ -102,16 +102,7 @@ module GitFit
102
102
  raise GitFit::Sync::AuthError, "Unexpected login response: #{resp_type}" unless resp_type == 'MFA_REQUIRED'
103
103
 
104
104
  mfa_method = data.dig('customerMfaInfo', 'mfaLastMethodUsed') || 'email'
105
- warn "MFA required (method: #{mfa_method})"
106
-
107
- code = mfa_code.to_s.strip
108
- if code.empty? && $stdin.tty?
109
- warn 'Enter MFA code: '
110
- code = $stdin.gets.to_s.strip
111
- end
112
- if code.empty?
113
- raise GitFit::Sync::AuthError, 'MFA required but no code provided (set SYNC__GARMIN__MFA_CODE or use stdin)'
114
- end
105
+ code = resolve_mfa_code(email, mfa_code)
115
106
 
116
107
  warn 'verifying MFA code...'
117
108
  mfa_data = {
@@ -145,6 +136,24 @@ module GitFit
145
136
  url[/[?&]ticket=(ST-[^&\s]+)/, 1]
146
137
  end
147
138
 
139
+ def resolve_mfa_code(email, mfa_code)
140
+ code = mfa_code.to_s.strip
141
+ if code.empty?
142
+ warn "[garmin_auth_pw] Garmin sent a verification code to #{email}"
143
+ $stderr.write('Enter the 6-digit code: ') if $stdin.tty?
144
+ code = $stdin.gets.to_s.strip
145
+ end
146
+ return code unless code.empty?
147
+
148
+ raise GitFit::Sync::AuthError,
149
+ "MFA required — check your email (#{email}) for the code.\n" \
150
+ " This code is valid for a few minutes.\n\n" \
151
+ " To continue, choose one:\n" \
152
+ " echo \"<CODE>\" | git fit auth garmin #{AUTH_FLAG}\n" \
153
+ " git fit auth garmin #{AUTH_FLAG} --mfa-code <CODE>\n\n" \
154
+ " If the code has expired, request a new one: git fit auth garmin #{AUTH_FLAG}"
155
+ end
156
+
148
157
  def _random_delay(label)
149
158
  delay = rand(LOGIN_DELAY_MIN..LOGIN_DELAY_MAX)
150
159
  warn "[garmin_auth_pw] #{label}: waiting #{delay.round}s for Cloudflare..."
@@ -61,6 +61,7 @@ module GitFit
61
61
  def sso_token(mode)
62
62
  email, password = sso_credentials
63
63
  creds = { email: email, password: password, domain: domain }
64
+ creds[:mfa_code] = @options[:mfa_code] if mfa_code_provided?
64
65
  case mode
65
66
  when :A then single_strategy(:A, creds)
66
67
  when :B then single_strategy(:B, creds)
@@ -68,6 +69,11 @@ module GitFit
68
69
  end
69
70
  end
70
71
 
72
+ def mfa_code_provided?
73
+ code = @options[:mfa_code]
74
+ !code.nil? && !code.to_s.strip.empty?
75
+ end
76
+
71
77
  def sso_credentials
72
78
  cfg = @config.sync_config('garmin')
73
79
  email = cfg['email'].to_s
data/lib/git_fit/cli.rb CHANGED
@@ -77,6 +77,7 @@ module GitFit
77
77
  option :B, type: :boolean, desc: 'Garmin Strategy B: Playwright (default)'
78
78
  option :auto, type: :boolean, desc: 'Garmin auto: B first, fallback to A'
79
79
  option :sync, type: :boolean, desc: 'Garmin: promote cached token to auth_seed'
80
+ option :mfa_code, type: :string, desc: 'Garmin MFA code (from email), re-run to continue'
80
81
  def auth(source)
81
82
  if source == 'garmin'
82
83
  GitFit::Auth::Garmin.new(options, git_fit_config).call
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GitFit
4
- VERSION = '0.10.9'
4
+ VERSION = '0.10.10'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-fit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.9
4
+ version: 0.10.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lax