sqrl-client 0.1.0 → 0.1.2

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: 0fcfd84f13a0b97bdd7742c8ff8d7cc918cada5c3ce355f51233c77e69463aa6
4
- data.tar.gz: 0eef85f22ed8e989649837454f1ceb28994dfd3d3f71ac10b54dac0631ddbd93
3
+ metadata.gz: 8a9db39e917407e067ccc959b250dd36ee6d027177f8e733dd5938729c7be7bd
4
+ data.tar.gz: 5e0949bc3ea41694426a66690c6f76429c92f38e8125c08f14d99d7a0a36fcbf
5
5
  SHA512:
6
- metadata.gz: 896a8745c0a4fdcb39babdb3ad61776b377eac8d60a45a50eb5edbababc62e3e8139f83667edb7ab9cdcd67903c1fa26427dc24f0424b5bd9f2c1ddc05a06442
7
- data.tar.gz: 6651d2647ce80e827fe772770e69eae96bf46c1a7e9e4d1088e7c113759db729e9cafc0de2d7eac1c335a4c6cedf1843bc16fe9f4afc634f8605fa5e38e0ce29
6
+ metadata.gz: 9e712f1fbdca9d1db2ee81bb3a1fd2b0bb2a8594b01140b4ed0f752046db2f7beb08aa75fa15099b3c80b185c90b9b13354b9cc1db20c7c38bc171a7562dc3a6
7
+ data.tar.gz: e2bbb78aa9529c7e35b9dc2085499f98e413651599efc4010b80a33b5a5b4c4f42d0eef565141674099c6a6139f565015dc093247935c88cc4eb0f128e750897
data/CHANGELOG.md CHANGED
@@ -1,9 +1,38 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.0 — 2026-10-20
3
+ All notable changes to `sqrl-client` are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this
6
+ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Every gem in
7
+ the family shares one version number and is released in lockstep.
8
+
9
+ ## [Unreleased]
10
+
11
+ ## [0.1.2] - 2026-10-21
12
+
13
+ ### Changed
14
+
15
+ - After a transient error the agent retries with the fresh nut and stops as soon as two
16
+ successive replies carry the identical TIF, raising `LoginFailed` (reason `:transient`)
17
+ with GRC's "The SQRL login page has expired" message, as Gibson specified
18
+ (grc.sqrl:23543). `max_transient_retries` remains as an upper bound.
19
+ - The conformance runner's `transient_recovery` case accepts both conforming server
20
+ behaviours: recovery on retry, or the identical repeated transient error.
21
+ - Requires sqrl-core 0.1.2.
22
+
23
+ ## [0.1.1] - 2026-10-20
24
+
25
+ ### Changed
26
+
27
+ - Requires sqrl-core 0.1.1, which fixes the `NoMethodError` on Ruby 3.1 and 3.2. No other
28
+ changes.
29
+
30
+ ## [0.1.0] - 2026-10-20
4
31
 
5
32
  First release.
6
33
 
34
+ ### Added
35
+
7
36
  - `SQRL::Client::Agent`: login, query, disable, enable, remove against any SQRL site, with
8
37
  login-page discovery, previous-identity handling, `ask` and `sin` answering, transient
9
38
  retry and CPS handoff on the same cookie jar.
@@ -15,3 +44,8 @@ First release.
15
44
  - A 23-case server conformance suite usable from Ruby, RSpec (`it_behaves_like "a SQRL
16
45
  server"`) and the command line, with JUnit output.
17
46
  - Requires Ruby 3.1+ and `http-cookie`.
47
+
48
+ [Unreleased]: https://github.com/danielpclark/sqrl-ruby/compare/v0.1.2...HEAD
49
+ [0.1.2]: https://github.com/danielpclark/sqrl-ruby/compare/v0.1.1...v0.1.2
50
+ [0.1.1]: https://github.com/danielpclark/sqrl-ruby/compare/v0.1.0...v0.1.1
51
+ [0.1.0]: https://github.com/danielpclark/sqrl-ruby/releases/tag/v0.1.0
@@ -88,6 +88,9 @@ module SQRL
88
88
  class Agent
89
89
  # Seconds between status polls for non-CPS logins.
90
90
  DEFAULT_POLL_INTERVAL = 0.5
91
+ # The message GRC's reference client shows when a login page has gone stale
92
+ # (Gibson, grc.sqrl:21887, 7 Feb 2019).
93
+ TRANSIENT_MESSAGE = "The SQRL login page has expired. Please refresh the page, then log in with SQRL."
91
94
 
92
95
  attr_reader :identity, :transport, :options, :alt_id, :logger
93
96
  attr_accessor :cps, :follow_cps, :unknown_site, :ask_handler, :max_transient_retries, :poll, :poll_interval,
@@ -209,11 +212,10 @@ module SQRL
209
212
  break unless session.state.transient?
210
213
 
211
214
  transient += 1
212
- next unless transient > @max_transient_retries
215
+ next unless transient > @max_transient_retries || repeated_error?(ctx)
213
216
 
214
- raise LoginFailed.new("the server kept answering with a transient error", reason: :transient,
215
- tif: session.last_response.tif,
216
- state: session.state)
217
+ raise LoginFailed.new(TRANSIENT_MESSAGE, reason: :transient, tif: session.last_response.tif,
218
+ state: session.state)
217
219
  end
218
220
 
219
221
  finish(ctx, command)
@@ -237,10 +239,10 @@ module SQRL
237
239
  case state.kind
238
240
  when :transient
239
241
  transient += 1
240
- next if transient <= @max_transient_retries
242
+ next if transient <= @max_transient_retries && !repeated_error?(ctx)
241
243
 
242
- raise LoginFailed.new("the server kept answering with a transient error", reason: :transient,
243
- tif: session.last_response.tif, state: state)
244
+ raise LoginFailed.new(TRANSIENT_MESSAGE, reason: :transient, tif: session.last_response.tif,
245
+ state: state)
244
246
  when :unknown
245
247
  next if session.has_previous? && session.try_next_previous
246
248
 
@@ -258,6 +260,13 @@ module SQRL
258
260
  end
259
261
  end
260
262
 
263
+ # "In the case that two successive TIF errors exactly match, the SQRL client MUST abort the
264
+ # transaction and ask the user to please refresh their login page" (Gibson, grc.sqrl:23543,
265
+ # 23 Nov 2019; On The Wire v1.07, "Transient Error Details").
266
+ def repeated_error?(ctx)
267
+ ctx.tifs.length >= 2 && ctx.tifs[-1] == ctx.tifs[-2]
268
+ end
269
+
261
270
  def answer_ask(ctx)
262
271
  ask = ctx.session.pending_ask
263
272
  return unless ask
@@ -451,15 +451,26 @@ module SQRL
451
451
 
452
452
  # ---- robustness --------------------------------------------------------------------
453
453
 
454
+ # A stale nut must draw a transient error (0x20) with a fresh nut. What happens on the
455
+ # retry depends on the server (On The Wire v1.07, "Transient Error Details"): a server
456
+ # that can tie the stale nut back to its browser session lets the retry succeed; a
457
+ # server that keeps its state by reference answers with exactly the same error, which
458
+ # tells the client to have the user refresh the page. Both are conforming; anything else
459
+ # (a different error, or a "success" nobody's browser can complete) is not.
454
460
  def case_transient_recovery(ctx)
455
461
  id = identity
456
462
  browser = transport
457
463
  page = @site.login_page(browser)
458
464
  bogus = page.sqrl_url.sub(/nut=[^&]+/, "nut=conformance-stale")
459
- result = agent(id, transport: browser, cps: false, poll: false).login(bogus)
460
- ctx.transcript.concat(result.transcript)
461
- check(result.tif_history.first.transient_error?, "the stale nut did not produce a transient error")
462
- check(result.authenticated?, "login did not recover from the transient error")
465
+ begin
466
+ result = agent(id, transport: browser, cps: false, poll: false).login(bogus)
467
+ ctx.transcript.concat(result.transcript)
468
+ check(result.tif_history.first.transient_error?, "the stale nut did not produce a transient error")
469
+ check(result.authenticated?, "login did not recover from the transient error")
470
+ rescue LoginFailed => e
471
+ check(e.reason == :transient && e.tif&.transient_error?,
472
+ "the stale nut did not produce a transient error (#{e.message})")
473
+ end
463
474
  end
464
475
 
465
476
  def case_concurrent_logins(ctx)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqrl-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel P. Clark
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.1.0
32
+ version: 0.1.2
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.1.0
39
+ version: 0.1.2
40
40
  description: Identity management, the full SQRL command set, CPS handoff, login-page
41
41
  discovery, a Capybara helper, a CLI and a server conformance suite.
42
42
  email: