activeadmin-oidc 2.1.3 → 2.2.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/README.md +99 -0
- data/app/controllers/active_admin/oidc/devise/omniauth_callbacks_controller.rb +45 -9
- data/app/views/active_admin/devise/sessions/new.html.erb +14 -2
- data/lib/activeadmin/oidc/configuration.rb +66 -0
- data/lib/activeadmin/oidc/engine.rb +30 -3
- data/lib/activeadmin/oidc/version.rb +1 -1
- data/lib/generators/active_admin/oidc/install/templates/initializer.rb.tt +27 -0
- data/lib/generators/active_admin/oidc/install/templates/sessions_new.html.erb +13 -4
- data/lib/generators/active_admin/oidc/install/templates/sessions_new_v4.html.erb +16 -7
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9047da541972355d3eca04483c7b57d51b2fcc931dda10e188cb4cf0e3ba0723
|
|
4
|
+
data.tar.gz: b5422c866ff24c4dc3a11ae11d5c70e229494fce603af8a2d1351b6f2565ecab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f7ef072f775050bbb777a855180e7bfe0736919c08b5eec238368be1bccae360fd16df88fc12f3f18ea87bf5ee332b48db6db5458dfc52421366dd8722f58931
|
|
7
|
+
data.tar.gz: e2497c94cff51a8f6451ab92b8a8ae0969b152bcef9a46d657e59258bf7e5ac6e5faee4e852c992eb22593b50581dd39bae1ad0dd76d5452ff9de87db6a36324
|
data/README.md
CHANGED
|
@@ -112,6 +112,10 @@ ActiveAdmin::Oidc.configure do |c|
|
|
|
112
112
|
# By default PKCE is enabled iff client_secret is blank. Override:
|
|
113
113
|
# c.pkce = true
|
|
114
114
|
|
|
115
|
+
# --- Stub login (development only) ------------------------------------
|
|
116
|
+
# See "Stub login" below. A no-op outside the development environment.
|
|
117
|
+
# c.stub_dev_env_login!
|
|
118
|
+
|
|
115
119
|
# --- Authorization hook (REQUIRED) ------------------------------------
|
|
116
120
|
c.on_login = ->(admin_user, claims) {
|
|
117
121
|
# ... see "The on_login hook" below
|
|
@@ -137,6 +141,8 @@ end
|
|
|
137
141
|
| `access_denied_message` | generic | Flash shown on any denial |
|
|
138
142
|
| `on_login` | — (required) | Authorization hook; see below |
|
|
139
143
|
|
|
144
|
+
`stub_dev_env_login!` is a method, not an option — see "Stub login" below.
|
|
145
|
+
|
|
140
146
|
## The `on_login` hook
|
|
141
147
|
|
|
142
148
|
`on_login` is the **only** place authorization lives. The gem handles authentication (the user proved who they are via the IdP); deciding whether that user is allowed into the admin panel — and what they can see once they are in — is the host application's problem. The gem does not ship a role model.
|
|
@@ -243,8 +249,101 @@ AdminUser.last.oidc_raw_info
|
|
|
243
249
|
* Clicking it POSTs to `/admin/auth/oidc` with a Rails CSRF token. The gem loads `omniauth-rails_csrf_protection` so OmniAuth 2.x delegates its authenticity check to Rails' forgery protection and `button_to` just works.
|
|
244
250
|
* After a successful callback the user is signed in and redirected to `/admin` (not the host app's `/`, which may not exist).
|
|
245
251
|
* **Disabled/locked users are rejected.** Devise's `active_for_authentication?` is checked after provisioning but before sign-in. If your model overrides this method (e.g. to check an `enabled` flag or Devise's `:lockable` module), the guard fires on OIDC sign-in too — the user sees an appropriate flash and is redirected to the login page.
|
|
252
|
+
* In development, `stub_dev_env_login!` repoints that same button at a local sign-in that never contacts the IdP — see "Stub login" below.
|
|
246
253
|
* Logout goes through Devise's stock session destroy. No RP-initiated single-logout ping to the IdP — override the destroy action in your host app if you need that.
|
|
247
254
|
|
|
255
|
+
## Stub login (development)
|
|
256
|
+
|
|
257
|
+
When OIDC is the only way in, local development gets harder than the feature
|
|
258
|
+
you were trying to build. Some providers accept a `localhost:3000` redirect
|
|
259
|
+
URI — until you need a different port, or a second app that also speaks OIDC on
|
|
260
|
+
the same machine. Registering and juggling those redirect URIs is work that has
|
|
261
|
+
nothing to do with the change you are making.
|
|
262
|
+
|
|
263
|
+
Stub login is the escape hatch. Turn it on and the login page renders exactly as
|
|
264
|
+
it always does — same button, same label — but the button signs in with locally
|
|
265
|
+
fabricated claims instead of redirecting to the IdP. A red warning sits above
|
|
266
|
+
it while it is on.
|
|
267
|
+
|
|
268
|
+
```ruby
|
|
269
|
+
ActiveAdmin::Oidc.configure do |c|
|
|
270
|
+
c.on_login = ->(admin_user, claims) {
|
|
271
|
+
groups = Array(claims["groups"])
|
|
272
|
+
return false unless groups.include?(ADMIN_GROUP)
|
|
273
|
+
|
|
274
|
+
admin_user.super_admin = groups.include?("super-admins")
|
|
275
|
+
true
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
# Default claims: { "sub" => "stub-uid", "email" => "stub-dev@example.com" }
|
|
279
|
+
# The optional block patches or replaces them.
|
|
280
|
+
c.stub_dev_env_login! do |claims|
|
|
281
|
+
claims.merge("groups" => [ADMIN_GROUP])
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
`stub_dev_env_login!` is a **no-op outside the development environment** and
|
|
287
|
+
returns `false` there. That is the whole safety story: nothing to flip off
|
|
288
|
+
before a deploy, no boot guard to trip, and no route drawn anywhere else.
|
|
289
|
+
Leave the call uncommented in the initializer if you want.
|
|
290
|
+
|
|
291
|
+
The block runs once per sign-in, not once at boot. To switch between distinct
|
|
292
|
+
users, vary both `sub` and the configured identity claim; changing only the
|
|
293
|
+
email retains `stub-uid` and updates the same `(provider, uid)` row.
|
|
294
|
+
|
|
295
|
+
### It is not a separate code path
|
|
296
|
+
|
|
297
|
+
The button POSTs to the gem's own callbacks controller, which hands the claims
|
|
298
|
+
to the **same `UserProvisioner` a real callback uses**. That means identity
|
|
299
|
+
lookup by `(provider, uid)`, the account-takeover guard, your `on_login` hook,
|
|
300
|
+
`oidc_raw_info`, and Devise's `active_for_authentication?` check all still run.
|
|
301
|
+
Authorization you develop against the stub is the authorization you get against
|
|
302
|
+
the IdP.
|
|
303
|
+
|
|
304
|
+
Three consequences worth internalising:
|
|
305
|
+
|
|
306
|
+
* **Your `on_login` hook must accept the stub claims.** If it reads
|
|
307
|
+
`claims["groups"]` or Zitadel's nested roles claim, add them in the block —
|
|
308
|
+
otherwise the stub is denied, correctly. You will find out the first time you
|
|
309
|
+
click the button, and fix it once.
|
|
310
|
+
* **Use a made-up email, never your real one.** A stub sign-in writes the fake
|
|
311
|
+
`sub` onto that row. Your real sign-in later brings the real `sub`, they do not
|
|
312
|
+
match, and the takeover guard locks you out of that account until someone
|
|
313
|
+
clears the columns by hand.
|
|
314
|
+
* **`provider` is always `"oidc"`,** never a separate `"stub"`/`"test"` value,
|
|
315
|
+
so the row a stub sign-in creates is an ordinary OIDC row.
|
|
316
|
+
|
|
317
|
+
### Limits
|
|
318
|
+
|
|
319
|
+
* **Development only.** Nothing else enables it.
|
|
320
|
+
* **Devise mounted inside an isolated engine is not supported.** The stub URL is
|
|
321
|
+
built as `"#{login_path}/stub"`, which does not account for a mount prefix.
|
|
322
|
+
The real SSO flow is unaffected.
|
|
323
|
+
* **CSRF protection is your app's.** The stub POST does not go through the
|
|
324
|
+
OmniAuth stack, so it relies on whatever your `ApplicationController` does. If
|
|
325
|
+
you have set `allow_forgery_protection = false` in `development.rb`, any web
|
|
326
|
+
page you visit can silently sign you into your local admin panel.
|
|
327
|
+
|
|
328
|
+
### Hosts with a custom login view
|
|
329
|
+
|
|
330
|
+
If your app ships its own `app/views/active_admin/devise/sessions/new.html.erb`,
|
|
331
|
+
the engine backs off and your view wins, so you render the button yourself.
|
|
332
|
+
Everything you need is on the config object — no helpers to mix in:
|
|
333
|
+
|
|
334
|
+
```erb
|
|
335
|
+
<% if ActiveAdmin::Oidc.config.stub_dev_env_login_enabled? %>
|
|
336
|
+
<p>Stub login is enabled: this button never contacts the identity provider.</p>
|
|
337
|
+
<% end %>
|
|
338
|
+
|
|
339
|
+
<%= button_to ActiveAdmin::Oidc.config.login_button_label,
|
|
340
|
+
ActiveAdmin::Oidc.config.login_submit_path,
|
|
341
|
+
method: :post, data: { turbo: false } %>
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
`login_submit_path` returns the stub route while stub login is on and the real
|
|
345
|
+
OmniAuth entry point otherwise.
|
|
346
|
+
|
|
248
347
|
## Custom login view
|
|
249
348
|
|
|
250
349
|
The gem ships a minimal SSO-only login page (a single button, no email/password fields). If you need a different layout — for instance, different branding, an explanatory paragraph, or multiple OmniAuth strategies — drop your own template at:
|
|
@@ -32,6 +32,50 @@ module ActiveAdmin
|
|
|
32
32
|
claims['sub'] = auth['uid'] if claims['sub'].blank? && auth['uid'].present?
|
|
33
33
|
claims['email'] = info['email'] if claims['email'].blank? && info['email'].present?
|
|
34
34
|
|
|
35
|
+
provision_and_sign_in(claims)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Development stub login: sign in with locally fabricated claims
|
|
39
|
+
# instead of a real authorization code flow, for machines whose
|
|
40
|
+
# redirect URI the IdP does not know. Deliberately runs the SAME
|
|
41
|
+
# provisioning path as `#oidc` -- identity lookup, the takeover
|
|
42
|
+
# guard, the host's `on_login` hook, oidc_raw_info, and the
|
|
43
|
+
# active_for_authentication? check all still apply, so what works
|
|
44
|
+
# locally is what works against the real IdP. In particular, a
|
|
45
|
+
# claims block that does not satisfy `on_login` is denied here
|
|
46
|
+
# exactly as the real IdP would deny it.
|
|
47
|
+
#
|
|
48
|
+
# The route only exists when stub login is enabled, and
|
|
49
|
+
# `stub_dev_env_login!` only enables it in the development
|
|
50
|
+
# environment. The check here covers a flag flipped at runtime.
|
|
51
|
+
def stub
|
|
52
|
+
cfg = ActiveAdmin::Oidc.config
|
|
53
|
+
unless cfg.stub_dev_env_login_enabled?
|
|
54
|
+
head :not_found
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
claims = cfg.stub_dev_env_login_claims
|
|
59
|
+
|
|
60
|
+
ActiveAdmin::Oidc.logger.warn(
|
|
61
|
+
"[activeadmin-oidc] STUB LOGIN used for sub=#{claims['sub'].inspect} " \
|
|
62
|
+
'-- no identity provider was contacted.'
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
provision_and_sign_in(claims, kind: 'stub login (no IdP)')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def failure
|
|
69
|
+
Rails.logger.warn("[activeadmin-oidc] omniauth failure: #{failure_message}")
|
|
70
|
+
flash[:alert] = ActiveAdmin::Oidc.config.access_denied_message
|
|
71
|
+
redirect_to after_omniauth_failure_path_for(resource_name)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# Shared tail of every sign-in path: turn a claims hash into a
|
|
77
|
+
# persisted, signed-in admin user, or into a denial flash.
|
|
78
|
+
def provision_and_sign_in(claims, kind: 'OIDC')
|
|
35
79
|
admin_user = UserProvisioner.new(
|
|
36
80
|
ActiveAdmin::Oidc.config,
|
|
37
81
|
claims: claims,
|
|
@@ -39,7 +83,7 @@ module ActiveAdmin
|
|
|
39
83
|
).call
|
|
40
84
|
|
|
41
85
|
sign_in_and_redirect admin_user, event: :authentication
|
|
42
|
-
set_flash_message(:notice, :success, kind:
|
|
86
|
+
set_flash_message(:notice, :success, kind: kind) if is_navigational_format?
|
|
43
87
|
rescue ActiveAdmin::Oidc::InactiveError => e
|
|
44
88
|
Rails.logger.warn("[activeadmin-oidc] inactive: #{e.inactive_message_key}")
|
|
45
89
|
# Fall back to the standard `inactive` translation rather
|
|
@@ -57,14 +101,6 @@ module ActiveAdmin
|
|
|
57
101
|
redirect_to after_omniauth_failure_path_for(resource_name)
|
|
58
102
|
end
|
|
59
103
|
|
|
60
|
-
def failure
|
|
61
|
-
Rails.logger.warn("[activeadmin-oidc] omniauth failure: #{failure_message}")
|
|
62
|
-
flash[:alert] = ActiveAdmin::Oidc.config.access_denied_message
|
|
63
|
-
redirect_to after_omniauth_failure_path_for(resource_name)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
private
|
|
67
|
-
|
|
68
104
|
# Land on the ActiveAdmin namespace root after a successful SSO
|
|
69
105
|
# sign-in instead of Devise's default (host app root). Hosts
|
|
70
106
|
# that don't define a `/` route would otherwise hit a routing
|
|
@@ -4,8 +4,14 @@
|
|
|
4
4
|
<%= site_title %> <%= set_page_title t('active_admin.devise.login.title') %>
|
|
5
5
|
</h2>
|
|
6
6
|
|
|
7
|
+
<% if ActiveAdmin::Oidc.config.stub_dev_env_login_enabled? %>
|
|
8
|
+
<p class="activeadmin-oidc-stub-login text-sm font-semibold border border-red-400 bg-red-50 text-red-900 rounded-md p-3 dark:bg-red-900/30 dark:border-red-700 dark:text-red-100">
|
|
9
|
+
Stub login is enabled: the button below signs in without contacting the identity provider.
|
|
10
|
+
</p>
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
7
13
|
<%= button_to ActiveAdmin::Oidc.config.login_button_label,
|
|
8
|
-
|
|
14
|
+
ActiveAdmin::Oidc.config.login_submit_path,
|
|
9
15
|
method: :post,
|
|
10
16
|
class: "activeadmin-oidc-login-button w-full",
|
|
11
17
|
form_class: 'formtastic',
|
|
@@ -15,7 +21,13 @@
|
|
|
15
21
|
<div id="login">
|
|
16
22
|
<h2><%= active_admin_application.site_title(self) %></h2>
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
<% if ActiveAdmin::Oidc.config.stub_dev_env_login_enabled? %>
|
|
25
|
+
<p class="activeadmin-oidc-stub-login" style="margin-top:1em;padding:0.75em;border:1px solid #c00;background:#fff3f3;color:#900;font-weight:bold">
|
|
26
|
+
Stub login is enabled: the button below signs in without contacting the identity provider.
|
|
27
|
+
</p>
|
|
28
|
+
<% end %>
|
|
29
|
+
|
|
30
|
+
<%= form_tag ActiveAdmin::Oidc.config.login_submit_path,
|
|
19
31
|
method: :post,
|
|
20
32
|
class: "activeadmin-oidc-login-form formtastic",
|
|
21
33
|
data: { turbo: false } do %>
|
|
@@ -13,6 +13,10 @@ module ActiveAdmin
|
|
|
13
13
|
'Your account has no permission to access this admin panel.'
|
|
14
14
|
DEFAULT_LOGIN_PATH = '/admin/login'
|
|
15
15
|
DEFAULT_LOGOUT_PATH = '/admin/logout'
|
|
16
|
+
DEFAULT_STUB_DEV_ENV_LOGIN_CLAIMS = {
|
|
17
|
+
'sub' => 'stub-uid',
|
|
18
|
+
'email' => 'stub-dev@example.com'
|
|
19
|
+
}.freeze
|
|
16
20
|
|
|
17
21
|
attr_accessor :issuer, :client_id, :client_secret, :scope,
|
|
18
22
|
:redirect_uri,
|
|
@@ -21,6 +25,12 @@ module ActiveAdmin
|
|
|
21
25
|
:access_denied_message, :on_login, :admin_user_class,
|
|
22
26
|
:login_path, :logout_path
|
|
23
27
|
|
|
28
|
+
# Readers, not writers: stub login is turned on through
|
|
29
|
+
# `stub_dev_env_login!` so the environment check cannot be skipped.
|
|
30
|
+
# Specs (this gem's own included) stub these two instead of
|
|
31
|
+
# pretending to run in the development environment.
|
|
32
|
+
attr_reader :stub_dev_env_login_claims_block
|
|
33
|
+
|
|
24
34
|
def initialize
|
|
25
35
|
reset!
|
|
26
36
|
end
|
|
@@ -41,6 +51,8 @@ module ActiveAdmin
|
|
|
41
51
|
@logout_path = DEFAULT_LOGOUT_PATH
|
|
42
52
|
@on_login = nil
|
|
43
53
|
@pkce_override = nil
|
|
54
|
+
@stub_dev_env_login = false
|
|
55
|
+
@stub_dev_env_login_claims_block = nil
|
|
44
56
|
self
|
|
45
57
|
end
|
|
46
58
|
|
|
@@ -54,6 +66,60 @@ module ActiveAdmin
|
|
|
54
66
|
@pkce_override = value
|
|
55
67
|
end
|
|
56
68
|
|
|
69
|
+
# Turns on the development stub login: the login page's button
|
|
70
|
+
# signs in with locally fabricated claims instead of redirecting to
|
|
71
|
+
# the IdP. For machines whose redirect URI the IdP does not know --
|
|
72
|
+
# a non-default port, or two apps sharing one OIDC client.
|
|
73
|
+
#
|
|
74
|
+
# A no-op outside the development environment, so there is nothing
|
|
75
|
+
# to guard at boot and nothing to flip off before a deploy.
|
|
76
|
+
#
|
|
77
|
+
# The optional block receives the default claims and returns the
|
|
78
|
+
# claims to sign in with, so a host whose `on_login` reads roles or
|
|
79
|
+
# groups can satisfy it:
|
|
80
|
+
#
|
|
81
|
+
# c.stub_dev_env_login! { |claims| claims.merge('groups' => ADMIN_GROUP) }
|
|
82
|
+
#
|
|
83
|
+
# The claims go through the same UserProvisioner as a real
|
|
84
|
+
# callback, so a block that does not satisfy `on_login` is denied
|
|
85
|
+
# exactly as the real IdP would deny it.
|
|
86
|
+
def stub_dev_env_login!(&block)
|
|
87
|
+
return false unless ::Rails.env.development?
|
|
88
|
+
|
|
89
|
+
@stub_dev_env_login = true
|
|
90
|
+
@stub_dev_env_login_claims_block = block
|
|
91
|
+
true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def stub_dev_env_login_enabled?
|
|
95
|
+
@stub_dev_env_login
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Evaluated once per stub sign-in, in the controller. String keys
|
|
99
|
+
# all the way down, the same shape `on_login` receives from a real
|
|
100
|
+
# callback.
|
|
101
|
+
#
|
|
102
|
+
# The block may either return a Hash or mutate the one it is given
|
|
103
|
+
# -- `claims["groups"] = [...]` as a last line returns the assigned
|
|
104
|
+
# value, not the Hash, and that should not 500 the dev's login.
|
|
105
|
+
def stub_dev_env_login_claims
|
|
106
|
+
claims = DEFAULT_STUB_DEV_ENV_LOGIN_CLAIMS.dup
|
|
107
|
+
block = stub_dev_env_login_claims_block
|
|
108
|
+
if block
|
|
109
|
+
returned = block.call(claims)
|
|
110
|
+
claims = returned if returned.is_a?(Hash)
|
|
111
|
+
end
|
|
112
|
+
claims.deep_transform_keys(&:to_s)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Where the login page's single button POSTs to: the stub route
|
|
116
|
+
# while stub login is on, the real OmniAuth entry point otherwise.
|
|
117
|
+
def login_submit_path
|
|
118
|
+
return "#{login_path}/stub" if stub_dev_env_login_enabled?
|
|
119
|
+
|
|
120
|
+
"#{::OmniAuth.config.path_prefix}/#{Engine::PROVIDER_NAME}"
|
|
121
|
+
end
|
|
122
|
+
|
|
57
123
|
def validate!
|
|
58
124
|
raise ConfigurationError, 'issuer is required' if issuer.blank?
|
|
59
125
|
raise ConfigurationError, 'client_id is required' if client_id.blank?
|
|
@@ -145,9 +145,12 @@ module ActiveAdmin
|
|
|
145
145
|
app.config.after_initialize do
|
|
146
146
|
next unless Engine.oidc_enabled?
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
# Captured here, not inside the append block: on ActiveAdmin 4
|
|
149
|
+
# routes are drawn lazily on the first request, long after the
|
|
150
|
+
# host initializer that set these ran, so a draw-time read can
|
|
151
|
+
# observe a config that something else has since replaced.
|
|
152
|
+
login_path = ActiveAdmin::Oidc.config.login_path
|
|
153
|
+
logout_path = ActiveAdmin::Oidc.config.logout_path
|
|
151
154
|
scope_name = Engine.admin_user_class.model_name.singular.to_sym
|
|
152
155
|
|
|
153
156
|
Engine.session_routes_target(app).append do
|
|
@@ -174,6 +177,30 @@ module ActiveAdmin
|
|
|
174
177
|
logout_via = [*::Devise.sign_out_via, aa_method].compact.uniq
|
|
175
178
|
|
|
176
179
|
match logout_path, to: ::ActiveAdmin::Devise::SessionsController.action(:destroy), as: :"destroy_#{scope_name}_session", via: logout_via
|
|
180
|
+
|
|
181
|
+
# Development stub login. `stub_dev_env_login!` only ever
|
|
182
|
+
# sets the flag in the development environment, so the
|
|
183
|
+
# route simply does not exist anywhere else -- and a route
|
|
184
|
+
# that is never drawn cannot be probed.
|
|
185
|
+
# Read through `ActiveAdmin::Oidc.config` at draw time rather
|
|
186
|
+
# than from a captured object: `reset!` swaps the whole
|
|
187
|
+
# Configuration instance, and a redraw must see the current
|
|
188
|
+
# one.
|
|
189
|
+
stub_cfg = ActiveAdmin::Oidc.config
|
|
190
|
+
|
|
191
|
+
if stub_cfg.stub_dev_env_login_enabled?
|
|
192
|
+
# Resolve the controller per request instead of capturing
|
|
193
|
+
# `.action(:stub)` at draw time: the class lives in the
|
|
194
|
+
# engine's app/ and is reloadable, and stub login runs in
|
|
195
|
+
# development where a captured constant goes stale on the
|
|
196
|
+
# first reload. A lambda also sidesteps the module scoping
|
|
197
|
+
# an isolated engine would apply to a string target.
|
|
198
|
+
# `login_submit_path` is what the login button posts to,
|
|
199
|
+
# so drawing the route from it keeps the two in step.
|
|
200
|
+
post stub_cfg.login_submit_path,
|
|
201
|
+
to: ->(env) { ::ActiveAdmin::Oidc::Devise::OmniauthCallbacksController.action(:stub).call(env) },
|
|
202
|
+
as: :"#{scope_name}_stub_login"
|
|
203
|
+
end
|
|
177
204
|
end
|
|
178
205
|
end
|
|
179
206
|
end
|
|
@@ -21,6 +21,33 @@ ActiveAdmin::Oidc.configure do |c|
|
|
|
21
21
|
# --- Login button label ----------------------------------------------
|
|
22
22
|
# c.login_button_label = "Sign in with Corporate SSO"
|
|
23
23
|
|
|
24
|
+
# --- Stub login (development only) ------------------------------------
|
|
25
|
+
# Makes the login button sign in with locally fabricated claims instead
|
|
26
|
+
# of redirecting to the IdP. Nothing is automatic: the login page still
|
|
27
|
+
# renders and you still click. Useful when the IdP does not know your
|
|
28
|
+
# local redirect URI -- a non-default port, or two apps sharing one
|
|
29
|
+
# OIDC client.
|
|
30
|
+
#
|
|
31
|
+
# A no-op outside the development environment, so it is safe to leave
|
|
32
|
+
# uncommented. A red warning sits above the button while it is on.
|
|
33
|
+
#
|
|
34
|
+
# The claims run through the normal pipeline, `on_login` included, so
|
|
35
|
+
# authorization behaves exactly as it will against the real IdP.
|
|
36
|
+
# Default claims: { "sub" => "stub-uid", "email" => "stub-dev@example.com" }
|
|
37
|
+
#
|
|
38
|
+
# c.stub_dev_env_login!
|
|
39
|
+
#
|
|
40
|
+
# Pass a block to patch or replace the claims -- needed when your
|
|
41
|
+
# `on_login` reads roles, groups or anything else:
|
|
42
|
+
#
|
|
43
|
+
# c.stub_dev_env_login! do |claims|
|
|
44
|
+
# claims.merge("groups" => ["admins"])
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
# Use a made-up email, not your real SSO one. A stub sign-in stamps the
|
|
48
|
+
# row with uid "stub-uid", and a later real sign-in for the same email
|
|
49
|
+
# is then refused by the takeover guard.
|
|
50
|
+
|
|
24
51
|
# --- on_login hook ---------------------------------------------------
|
|
25
52
|
# Called with (admin_user, claims) after identity lookup and before
|
|
26
53
|
# save. Mutate admin_user in place, return truthy to allow sign-in,
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
<%# Development stub login. The submit path switches to the stub
|
|
2
|
+
route while ActiveAdmin::Oidc.config.stub_dev_env_login! is on,
|
|
3
|
+
which only ever happens in the development environment. %>
|
|
1
4
|
<div id="login">
|
|
2
|
-
<h2
|
|
5
|
+
<h2><%= active_admin_application.site_title(self) %></h2>
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
<% if ActiveAdmin::Oidc.config.stub_dev_env_login_enabled? %>
|
|
8
|
+
<p class="activeadmin-oidc-stub-login" style="margin-top:1em;padding:0.75em;border:1px solid #c00;background:#fff3f3;color:#900;font-weight:bold">
|
|
9
|
+
Stub login is enabled: the button below signs in without contacting the identity provider.
|
|
10
|
+
</p>
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
13
|
+
<%= form_tag ActiveAdmin::Oidc.config.login_submit_path,
|
|
5
14
|
method: :post,
|
|
6
15
|
class: "activeadmin-oidc-login-form formtastic",
|
|
7
16
|
data: { turbo: false } do %>
|
|
8
|
-
|
|
17
|
+
<%= submit_tag ActiveAdmin::Oidc.config.login_button_label,
|
|
9
18
|
class: "activeadmin-oidc-login-button" %>
|
|
10
|
-
|
|
19
|
+
<% end %>
|
|
11
20
|
</div>
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
<%# Development stub login. The submit path switches to the stub
|
|
2
|
+
route while ActiveAdmin::Oidc.config.stub_dev_env_login! is on,
|
|
3
|
+
which only ever happens in the development environment. %>
|
|
1
4
|
<div class="p-6 sm:p-8 space-y-4 md:space-y-6 w-full sm:max-w-md bg-white sm:rounded-md shadow dark:border dark:bg-gray-800/50 dark:border-gray-800">
|
|
2
5
|
<h2 class="text-xl font-bold text-gray-900 md:text-2xl dark:text-white flex gap-2 items-center">
|
|
3
|
-
|
|
6
|
+
<%= site_title %> <%= set_page_title t('active_admin.devise.login.title') %>
|
|
4
7
|
</h2>
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
<% if ActiveAdmin::Oidc.config.stub_dev_env_login_enabled? %>
|
|
10
|
+
<p class="activeadmin-oidc-stub-login text-sm font-semibold border border-red-400 bg-red-50 text-red-900 rounded-md p-3 dark:bg-red-900/30 dark:border-red-700 dark:text-red-100">
|
|
11
|
+
Stub login is enabled: the button below signs in without contacting the identity provider.
|
|
12
|
+
</p>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<%= button_to ActiveAdmin::Oidc.config.login_button_label,
|
|
16
|
+
ActiveAdmin::Oidc.config.login_submit_path,
|
|
17
|
+
method: :post,
|
|
18
|
+
class: "activeadmin-oidc-login-button w-full",
|
|
19
|
+
form_class: 'formtastic',
|
|
20
|
+
data: { turbo: false } %>
|
|
12
21
|
</div>
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activeadmin-oidc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Igor Fedoronchuk
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-09-03 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: activeadmin
|
|
@@ -254,6 +255,7 @@ description: |
|
|
|
254
255
|
It builds on Devise + omniauth_openid_connect and adds JIT user provisioning,
|
|
255
256
|
role mapping from provider claims via a host-owned on_login hook, and a
|
|
256
257
|
single install generator that wires everything up.
|
|
258
|
+
email:
|
|
257
259
|
executables: []
|
|
258
260
|
extensions: []
|
|
259
261
|
extra_rdoc_files: []
|
|
@@ -278,6 +280,7 @@ licenses:
|
|
|
278
280
|
- MIT
|
|
279
281
|
metadata:
|
|
280
282
|
rubygems_mfa_required: 'true'
|
|
283
|
+
post_install_message:
|
|
281
284
|
rdoc_options: []
|
|
282
285
|
require_paths:
|
|
283
286
|
- lib
|
|
@@ -292,7 +295,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
292
295
|
- !ruby/object:Gem::Version
|
|
293
296
|
version: '0'
|
|
294
297
|
requirements: []
|
|
295
|
-
rubygems_version: 3.
|
|
298
|
+
rubygems_version: 3.5.22
|
|
299
|
+
signing_key:
|
|
296
300
|
specification_version: 4
|
|
297
301
|
summary: OpenID Connect SSO for ActiveAdmin
|
|
298
302
|
test_files: []
|