studio-engine 0.13.1 → 0.14.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1509550e891472f578f1472b33782de7104a592e8d325ad11aa520c0b600171a
4
- data.tar.gz: cac1e68d9950257d017a2f3c93d81733ac96e9ba27e9cf8b53cdfe06c9709552
3
+ metadata.gz: 8540d767e024bc8314d5471788d3f430e79a1e17c2dc49ba5c28a61f84f74646
4
+ data.tar.gz: ba45685b2e743504e7c60a59f35a6a41074d72b8ef1c60460f3f6b65d34d65f6
5
5
  SHA512:
6
- metadata.gz: 2880909b924c60c406aae3990240c47ef0d36cba7b4c34fb7b150dcbc1cd93a35d81d6bbb1cb4b65d22b832f7e83635ac68b0b26ded20bd099cf434e5773a066
7
- data.tar.gz: f87db04adcf27abd17485c52bbb1f5767269f50ed19e9ac66a808d9d95e0271adc1109eb64539173e81a603a3cae915d7b380282d2ab2fad3cf14bef0936933e
6
+ metadata.gz: 6dabad0d6e3946fb10b990a4b525cd3e03c248eabfebf1408cd790cfaba678df19fe115d7928d8eac247ada917cf9479e7857afe08418d9e9944b0ac7a5ed975
7
+ data.tar.gz: fbd613547387db6bd201d0f03d66685accffcba10adaa8f68349b3fbe785ea275e5c4c6a3f0dff2d15bdc7884294fa995066c7b20830319e54831219bdf6c10c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — `MAJOR.MINOR.PATCH`. Consumer Rails apps install the released RubyGems package with `gem "studio-engine", "~> 0.6"`; bumping the gem version and updating consumer lockfiles is a release.
4
4
 
5
+ ## 0.14.0 — 2026-07-22
6
+
7
+ ### Added
8
+
9
+ - **Passwordless engine login** — the stock `sessions/new` sign-in view now
10
+ renders its method set from `auth_methods`, so a host app configured for
11
+ passwordless sign-in gets the correct engine-shipped view with no local
12
+ override. Fixes a 500 in the passwordless path where the view assumed a
13
+ password field.
14
+
5
15
  ## 0.13.1 — 2026-07-19
6
16
 
7
17
  ### Changed
@@ -35,36 +35,60 @@
35
35
  <%= render "sessions/sso_continue" if defined?(sso_user_available?) && sso_user_available? %>
36
36
  <% end %>
37
37
 
38
- <%= form_tag login_path, method: :post do %>
39
- <div class="mb-5">
40
- <label for="email" class="block text-sm text-secondary mb-2 font-medium">Email</label>
41
- <input type="email" name="email" id="email" value="<%= params[:email] %>" <%= 'autofocus' unless has_sso %>
42
- x-ref="emailField"
43
- class="input-field"
44
- placeholder="you@example.com">
45
- </div>
38
+ <%# The primary email form renders from the app's configured auth_methods: a PASSWORD
39
+ form only when passwords are available (Studio.password_login_available? — enabled AND
40
+ the User supports `authenticate`), otherwise the passwordless MAGIC-LINK request form.
41
+ The old hardcoded password field 500'd on every passwordless app's only sign-in path. %>
42
+ <% if Studio.password_login_available? %>
43
+ <%= form_tag login_path, method: :post do %>
44
+ <div class="mb-5">
45
+ <label for="email" class="block text-sm text-secondary mb-2 font-medium">Email</label>
46
+ <input type="email" name="email" id="email" value="<%= params[:email] %>" <%= 'autofocus' unless has_sso %>
47
+ x-ref="emailField"
48
+ class="input-field"
49
+ placeholder="you@example.com">
50
+ </div>
46
51
 
47
- <div class="mb-6">
48
- <label for="password" class="block text-sm text-secondary mb-2 font-medium">Password</label>
49
- <input type="password" name="password" id="password"
50
- class="input-field"
51
- placeholder="Your password">
52
- </div>
52
+ <div class="mb-6">
53
+ <label for="password" class="block text-sm text-secondary mb-2 font-medium">Password</label>
54
+ <input type="password" name="password" id="password"
55
+ class="input-field"
56
+ placeholder="Your password">
57
+ </div>
53
58
 
54
- <button type="submit" class="btn btn-primary btn-lg w-full">
55
- Log In
56
- </button>
59
+ <button type="submit" class="btn btn-primary btn-lg w-full">
60
+ Log In
61
+ </button>
62
+ <% end %>
63
+ <% elsif Studio.auth_method?(:magic_link) %>
64
+ <%= form_tag magic_link_request_path, method: :post do %>
65
+ <div class="mb-5">
66
+ <label for="email" class="block text-sm text-secondary mb-2 font-medium">Email</label>
67
+ <input type="email" name="email" id="email" value="<%= params[:email] %>" <%= 'autofocus' unless has_sso %>
68
+ x-ref="emailField"
69
+ class="input-field"
70
+ placeholder="you@example.com">
71
+ </div>
72
+
73
+ <button type="submit" class="btn btn-primary btn-lg w-full">
74
+ Send sign-in link
75
+ </button>
76
+ <% end %>
57
77
  <% end %>
58
78
 
59
- <div class="relative my-6">
60
- <div class="absolute inset-0 flex items-center"><div class="w-full border-t border-subtle"></div></div>
61
- <div class="relative flex justify-center text-sm"><span class="bg-surface px-3 text-secondary">or</span></div>
62
- </div>
79
+ <% if Studio.auth_method?(:google) %>
80
+ <% if Studio.password_login_available? || Studio.auth_method?(:magic_link) %>
81
+ <div class="relative my-6">
82
+ <div class="absolute inset-0 flex items-center"><div class="w-full border-t border-subtle"></div></div>
83
+ <div class="relative flex justify-center text-sm"><span class="bg-surface px-3 text-secondary">or</span></div>
84
+ </div>
85
+ <% end %>
63
86
 
64
- <%= button_to "/auth/google_oauth2", method: :post, data: { turbo: false },
65
- class: "btn btn-google btn-lg w-full gap-3" do %>
66
- <%= render "components/google_logo" %>
67
- Sign in with Google
87
+ <%= button_to "/auth/google_oauth2", method: :post, data: { turbo: false },
88
+ class: "btn btn-google btn-lg w-full gap-3" do %>
89
+ <%= render "components/google_logo" %>
90
+ Sign in with Google
91
+ <% end %>
68
92
  <% end %>
69
93
 
70
94
  <p class="text-center text-secondary text-sm mt-6">
@@ -1,3 +1,3 @@
1
1
  module Studio
2
- VERSION = "0.13.1"
2
+ VERSION = "0.14.0"
3
3
  end
data/lib/studio.rb CHANGED
@@ -152,6 +152,28 @@ module Studio
152
152
  auth_methods.include?(method.to_sym)
153
153
  end
154
154
 
155
+ # True when the engine login should render a PASSWORD field/form: passwords are enabled
156
+ # (`:password` in auth_methods) AND the host User model actually supports them (responds to
157
+ # `authenticate` — i.e. `has_secure_password`). Both are required, and the second is the
158
+ # belt-and-suspenders: without it, a passwordless app (User with no `authenticate`) rendered
159
+ # the hardcoded password field and 500'd on submit via `user.authenticate` — the whole fleet
160
+ # having moved off passwords, this made the engine default wrong for every consumer. The User
161
+ # check is defensive of a mis-set auth_methods; the contract validation normally guarantees it
162
+ # (validate_user_contract! requires PASSWORD_USER_INSTANCE_METHODS iff auth_method?(:password)).
163
+ def self.password_login_available?
164
+ auth_method?(:password) && user_supports_password?
165
+ end
166
+
167
+ # Does the host User model respond to `authenticate` (has_secure_password)? Safe when no User
168
+ # is defined (an engine-only boot / a test with no host model) — answers false rather than raise.
169
+ def self.user_supports_password?
170
+ return false unless defined?(::User) && ::User.respond_to?(:instance_methods)
171
+
172
+ PASSWORD_USER_INSTANCE_METHODS.all? { |m| ::User.instance_methods.include?(m) }
173
+ rescue StandardError
174
+ false
175
+ end
176
+
155
177
  # True when the emailed/inbox magic-link URL is the short /l/<token> — i.e.
156
178
  # magic links are Studio::Link rows AND this app draws the /l routes. False =
157
179
  # the legacy /magic_link/<token> path: the :signed store, OR an app on the
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: studio-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex McRitchie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-20 00:00:00.000000000 Z
11
+ date: 2026-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails