kiosk-user-idp-devise 0.5.20
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 +7 -0
- data/CHANGELOG.md +122 -0
- data/LICENSE.txt +202 -0
- data/README.md +105 -0
- data/lib/kiosk/user_identity_providers/devise/configuration_extension.rb +19 -0
- data/lib/kiosk/user_identity_providers/devise/sessions_controller.rb +29 -0
- data/lib/kiosk/user_identity_providers/devise/version.rb +10 -0
- data/lib/kiosk/user_identity_providers/devise/wire_signpost.rb +41 -0
- data/lib/kiosk/user_identity_providers/devise.rb +77 -0
- data/lib/kiosk/user_identity_providers/devise_session.rb +109 -0
- data/lib/kiosk-user-idp-devise.rb +5 -0
- metadata +88 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: eaa5961003f556d23c16fae8b4715be095b5b75dcf922a4e45965789c0bfc9e5
|
|
4
|
+
data.tar.gz: c9ca748caede6c73d2582e2872a98a6e1631a2a4b2211c6dd1a80dc1fd7846c9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 21e4d2e6b82c2ff6ce89e8a7c201fb8f634c6f9562773a070719d28f39df2a6b8ab38297e27f34875b6d63efce9c51d4a2571b74296096ca07663bc8478c335c
|
|
7
|
+
data.tar.gz: a79089492e3efec13c759bd13ee2159970ba102aba9dd61289b449282c21a36d825881f400c6836313ab622efdbdccbb2f1a1e6869a6d46c6f43da0e84e7ce58
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/).
|
|
4
|
+
|
|
5
|
+
How to write an entry, and what a release section means: `CHANGELOG-RULE.md` at
|
|
6
|
+
the root of this repository —
|
|
7
|
+
<https://github.com/kiosk-hq/kiosk/blob/main/CHANGELOG-RULE.md>. In short: under
|
|
8
|
+
200 characters, one or two sentences, the essence rather than the content; write it
|
|
9
|
+
under `## [Unreleased]`; a cut renames that heading to
|
|
10
|
+
`## [MAJOR.MINOR.PATCH] — <date>` and opens a fresh empty one above it; nothing
|
|
11
|
+
already written is edited.
|
|
12
|
+
|
|
13
|
+
## [Unreleased]
|
|
14
|
+
|
|
15
|
+
## [0.5.20] — 2026-10-20
|
|
16
|
+
|
|
17
|
+
- 2026-10-20: Version 0.5.20, the tree cut that matches skill 0.5.20.
|
|
18
|
+
|
|
19
|
+
## [0.5.19] — 2026-10-20
|
|
20
|
+
|
|
21
|
+
- 2026-10-19: `DeviseSession` connects to an origin that names an IPv6 literal, such as `http://[::1]:3000`.
|
|
22
|
+
|
|
23
|
+
## [0.5.18] — 2026-10-18
|
|
24
|
+
|
|
25
|
+
- 2026-10-18: Version 0.5.18, the tree cut that matches skill 0.5.18.
|
|
26
|
+
|
|
27
|
+
## [0.5.17] — 2026-10-18
|
|
28
|
+
|
|
29
|
+
- 2026-10-18: Version 0.5.17, the tree cut that matches skill 0.5.17.
|
|
30
|
+
|
|
31
|
+
## [0.5.16] — 2026-10-18
|
|
32
|
+
|
|
33
|
+
- 2026-10-18: `Kiosk::UserIdentityProviders::DeviseSession#sign_out!` is removed.
|
|
34
|
+
## [0.5.15] — 2026-10-18
|
|
35
|
+
|
|
36
|
+
- The README names the human-side page gate and the link endpoints as the callers of the adapter.
|
|
37
|
+
|
|
38
|
+
## [0.5.14] — 2026-10-18
|
|
39
|
+
|
|
40
|
+
- 2026-10-18: Version 0.5.14, the tree cut that matches skill 0.5.14.
|
|
41
|
+
|
|
42
|
+
- 2026-10-17: `user_id_column` is a setting of this gem, default `:id`; `Kiosk.configure` sets it as before.
|
|
43
|
+
|
|
44
|
+
## [0.5.13] — 2026-10-11
|
|
45
|
+
|
|
46
|
+
- 2026-10-11: Version 0.5.13, the tree cut that matches skill 0.5.13.
|
|
47
|
+
|
|
48
|
+
- `WireSignpost` and `SessionsController` answer a JSON caller at the Devise sign-in and sign-out pages with a pointer to `/.well-known/kiosk.json`, so an operator need not write them.
|
|
49
|
+
|
|
50
|
+
## [0.5.12] — 2026-10-08
|
|
51
|
+
|
|
52
|
+
- 2026-10-08: Version 0.5.12, the tree cut that matches skill 0.5.12; this gem's surface is unchanged.
|
|
53
|
+
|
|
54
|
+
## [0.5.11] — 2026-10-08
|
|
55
|
+
|
|
56
|
+
- 2026-10-08: Version 0.5.11, the tree cut that matches skill 0.5.11; this gem's surface is unchanged.
|
|
57
|
+
|
|
58
|
+
## [0.5.10] — 2026-10-08
|
|
59
|
+
|
|
60
|
+
- 2026-10-08: Version 0.5.10, the tree cut that matches skill 0.5.10; this gem's surface is unchanged.
|
|
61
|
+
|
|
62
|
+
## [0.5.9] — 2026-10-08
|
|
63
|
+
|
|
64
|
+
- 2026-10-08: Version 0.5.9, the tree cut that matches skill 0.5.9; this gem's surface is unchanged.
|
|
65
|
+
|
|
66
|
+
## [0.5.8] — 2026-10-07
|
|
67
|
+
|
|
68
|
+
- 2026-10-07: Version 0.5.8, the tree cut that matches skill 0.5.8; this gem's surface is unchanged.
|
|
69
|
+
|
|
70
|
+
## [0.5.7] — 2026-10-07
|
|
71
|
+
|
|
72
|
+
- 2026-10-07: Version 0.5.7, the tree cut that matches skill 0.5.7; this gem's surface is unchanged.
|
|
73
|
+
|
|
74
|
+
## [0.5.6] — 2026-10-07
|
|
75
|
+
|
|
76
|
+
- 2026-10-07: Version 0.5.6, the tree cut that matches skill 0.5.6; this gem's surface is unchanged.
|
|
77
|
+
|
|
78
|
+
## [0.5.5] — 2026-10-07
|
|
79
|
+
|
|
80
|
+
- 2026-10-07: Version 0.5.5, the tree cut that matches skill 0.5.5; this gem's surface is unchanged.
|
|
81
|
+
|
|
82
|
+
## [0.5.4] — 2026-10-07
|
|
83
|
+
|
|
84
|
+
- 2026-10-07: Version 0.5.4, the tree cut that matches skill 0.5.4; this gem's surface is unchanged.
|
|
85
|
+
|
|
86
|
+
## [0.5.3] — 2026-10-06
|
|
87
|
+
|
|
88
|
+
- 2026-10-06: Version 0.5.3, the tree cut that matches skill 0.5.3; this gem's surface is unchanged.
|
|
89
|
+
|
|
90
|
+
## [0.5.2] — 2026-10-06
|
|
91
|
+
|
|
92
|
+
- 2026-10-06: Version 0.5.2, the tree cut that matches skill 0.5.2; this gem's surface is unchanged.
|
|
93
|
+
|
|
94
|
+
## [0.5.0] — 2026-09-26
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
|
|
98
|
+
- **`DeviseSession` reads TLS off the target's scheme.** It opened every socket in cleartext, so an `https://` server URL could not be dialled at all.
|
|
99
|
+
|
|
100
|
+
- README: every `gem` line in the install section now carries `github: "kiosk-hq/kiosk"`, so a copied line resolves before publication; the banner above them states what the block does rather than asking the reader to add it.
|
|
101
|
+
|
|
102
|
+
### Added
|
|
103
|
+
|
|
104
|
+
- `Kiosk::UserIdentityProviders::DeviseSession` (`require "kiosk/user_identity_providers/devise_session"`) — the CLIENT end of this adapter. `Devise` reads the Warden user off an incoming request; this drives the shipped Devise form to create the session that read finds: GET `/users/sign_in` for the cookie and the CSRF token, POST the credentials, keep the Set-Cookie. `session: true` is the only knob, and it is opt-in per request so an agent's Bearer call never carries the human's cookies.
|
|
105
|
+
|
|
106
|
+
- Initial skeleton — `Kiosk::UserIdentityProviders::Devise` extending `Kiosk::UserIdentityProviders::Base`.
|
|
107
|
+
- `#verify(request)` reads the signed-in user from the request's Warden proxy (`request.env["warden"].user` — the shipped wire, where kiosk-server's `IdentityResolution.resolve` passes an `ActionDispatch::Request`) and returns a `Kiosk::Identity` with `actor: "human"`. Returns `nil` when no user is signed in — which covers unauthenticated, locked, and unconfirmed users uniformly since Devise's `active_for_authentication?` already gates the Warden user.
|
|
108
|
+
- Also accepts a controller-shaped object exposing `#current_user`, and a bare Rack `env` Hash carrying `env["warden"]`, for hosts that pass either directly.
|
|
109
|
+
- Role resolution: `user.kiosk_role` if defined, else first of `Kiosk.configuration.roles`. Raises `Kiosk::UserIdentityProviders::Devise::ConfigurationError` with a help-style message when both fall through.
|
|
110
|
+
- Honours `Kiosk.configuration.user_id_column` (default `:id`).
|
|
111
|
+
- Inherits the default `user_active?` from {Kiosk::UserIdentityProviders::Base} — embedded mode relies on Devise's per-request `active_for_authentication?` hook rather than an opt-in callback.
|
|
112
|
+
|
|
113
|
+
### Design decisions
|
|
114
|
+
|
|
115
|
+
- **No hard `devise` runtime dependency.** The adapter only reads the request's Warden user (`request.env["warden"].user`); the provider's installed Devise satisfies the requirement. Documenting this in `README.md` is cleaner than forcing a Bundler constraint that doesn't match reality (kiosk-server's `IdentityResolution.resolve` passes an `ActionDispatch::Request` — Devise itself isn't a constructor argument).
|
|
116
|
+
- **No test-time `devise` dependency.** Tests use a stub `current_user`; loading Devise just to verify a one-line read would be ceremony.
|
|
117
|
+
|
|
118
|
+
### Out of scope for first release
|
|
119
|
+
|
|
120
|
+
- `Kiosk::HumanContext` cross-channel session mixing.
|
|
121
|
+
- Full Rails controller integration test (needs a host app — lands with kiosk-server's request specs).
|
|
122
|
+
- `kiosk-devise` — the broader Devise integration with kiosk-server's controllers, generators, and approval-form partial.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
data/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# kiosk-user-idp-devise
|
|
2
|
+
|
|
3
|
+
The Devise user-IdP adapter for [Kiosk](https://kiosk.tech) — for Rails providers that authenticate principals through Devise. Install it explicitly; it is not bundled by `kiosk-all`.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
Reads the signed-in user from the incoming request's Warden proxy and returns a `Kiosk::Identity` for the human-side surfaces: the device-verify page, the linked-assistants page, and `POST /kiosk/auth/link` and `/kiosk/auth/unlink`. The wire and the event stream never take it; they accept only the assistant's bearer token.
|
|
8
|
+
|
|
9
|
+
The adapter is **agnostic about how the user logged in**: Devise's `database_authenticatable` and `omniauthable` modules both populate the request's Warden user, so the same read covers password login, passwordless magic-link, and every OmniAuth strategy (Google, GitHub, SAML, …).
|
|
10
|
+
|
|
11
|
+
**Lockable / confirmable handling is implicit.** Devise's `active_for_authentication?` already gates the Warden user, so a locked or unconfirmed user yields no signed-in user and the request fails as unauthenticated. No extra code in the adapter.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
> **Not on RubyGems yet** — so every `gem` line below carries `github: "kiosk-hq/kiosk"`, which is what makes it copy-pasteable today. Publication status and the canonical install are stated once, in the monorepo README's [Install](https://github.com/kiosk-hq/kiosk#install) section.
|
|
16
|
+
|
|
17
|
+
Add the adapter explicitly — the `kiosk-all` meta-gem pulls in only `kiosk-core` and `kiosk-server`, so IdP adapters are opt-in per provider:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
gem "kiosk-user-idp-devise", github: "kiosk-hq/kiosk"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The adapter declares no hard runtime dependency on `devise` — it only reads the request's Warden user, and your provider's already-installed Devise satisfies the requirement.
|
|
24
|
+
|
|
25
|
+
## Wire up
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
# config/initializers/kiosk.rb
|
|
29
|
+
require "kiosk/user_identity_providers/devise"
|
|
30
|
+
|
|
31
|
+
Kiosk.configure do |c|
|
|
32
|
+
c.user_idp = Kiosk::UserIdentityProviders::Devise.new
|
|
33
|
+
c.roles = %i[customer]
|
|
34
|
+
# Required whenever `c.roles` is set, and refused at boot without it: the
|
|
35
|
+
# default role an assistant lands on when nothing else resolves one. Declaring
|
|
36
|
+
# no roles at all is the other supported shape, and then neither line is set.
|
|
37
|
+
c.registration_role = :customer
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Pointing assistants at the wire
|
|
42
|
+
|
|
43
|
+
An assistant that POSTs JSON at the sign-in page, or signs out with no session, gets a JSON body naming `/.well-known/kiosk.json` (`422 invalid_authenticity_token`, `401 not_signed_in`) instead of an empty error. Browsers are answered as before.
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
class ApplicationController < ActionController::Base
|
|
47
|
+
include Kiosk::UserIdentityProviders::Devise::WireSignpost
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
# config/routes.rb
|
|
53
|
+
devise_for :users, controllers: { sessions: "kiosk/user_identity_providers/devise/sessions" }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Driving the sign-in from a script
|
|
57
|
+
|
|
58
|
+
An origin wired to this adapter has no stub user-IdP, so anything that needs the HUMAN half of a ceremony — approving an assistant on the device-verify page, minting a link code, unlinking — has to hold a real browser session. `DeviseSession` is that session, and it is the client end of the same contract the adapter above serves:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
require "kiosk/user_identity_providers/devise_session"
|
|
62
|
+
|
|
63
|
+
session = Kiosk::UserIdentityProviders::DeviseSession.new(ENV.fetch("SERVER_URL"))
|
|
64
|
+
session.sign_in!(email: "alice@example.com", password: "…")
|
|
65
|
+
|
|
66
|
+
rc, link = session.post_json("/kiosk/auth/link", {}, { session: true })
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Nothing in it is a test double — it drives the shipped Devise routes over real HTTP exactly as a browser does. `session: true` is the only knob: it marks the calls that are the HUMAN's, so an agent's own Bearer calls never carry the human's cookie jar.
|
|
70
|
+
|
|
71
|
+
## Role resolution
|
|
72
|
+
|
|
73
|
+
Each Kiosk token carries exactly one active role. For a Devise-authenticated human:
|
|
74
|
+
|
|
75
|
+
1. If the user model defines `#kiosk_role`, that wins.
|
|
76
|
+
2. Otherwise, the first symbol in `Kiosk.configuration.roles` is used.
|
|
77
|
+
3. If `roles` is empty AND `#kiosk_role` is absent, `Kiosk::UserIdentityProviders::Devise::ConfigurationError` is raised.
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
class User < ApplicationRecord
|
|
81
|
+
devise :database_authenticatable
|
|
82
|
+
|
|
83
|
+
# Optional — only if you need per-user role choice.
|
|
84
|
+
def kiosk_role
|
|
85
|
+
support_staff? ? :customer_support : :customer
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Request shape
|
|
91
|
+
|
|
92
|
+
kiosk-server calls the adapter with an `ActionDispatch::Request` from the human-side page gate and from the link endpoints. The adapter reads the signed-in user from the Warden proxy of that request, `request.env["warden"].user`. Devise sets this proxy when its middleware has run. The adapter also accepts an object that has `#current_user`, such as a controller, and a bare Rack `env` Hash that has `env["warden"]`.
|
|
93
|
+
|
|
94
|
+
## Status
|
|
95
|
+
|
|
96
|
+
Pre-v1.0 alpha. API surface stable across pre-v1.0 minor bumps.
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
Apache-2.0 — see `LICENSE.txt`.
|
|
101
|
+
|
|
102
|
+
## Links
|
|
103
|
+
|
|
104
|
+
- [kiosk.tech](https://kiosk.tech)
|
|
105
|
+
- [Issue tracker](https://github.com/kiosk-hq/kiosk/issues)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kiosk
|
|
4
|
+
module UserIdentityProviders
|
|
5
|
+
class Devise
|
|
6
|
+
# The Devise adapter's setting, added to {Kiosk::Configuration}.
|
|
7
|
+
module ConfigurationExtension
|
|
8
|
+
# The user model's column that holds the customer's id. Default :id.
|
|
9
|
+
attr_writer :user_id_column
|
|
10
|
+
|
|
11
|
+
def user_id_column
|
|
12
|
+
@user_id_column ||= :id
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Kiosk::Configuration.include(Kiosk::UserIdentityProviders::Devise::ConfigurationExtension)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kiosk/user_identity_providers/devise/wire_signpost'
|
|
4
|
+
|
|
5
|
+
module Kiosk
|
|
6
|
+
module UserIdentityProviders
|
|
7
|
+
class Devise
|
|
8
|
+
# Devise's sign-in and sign-out, answering a JSON sign-out with no session
|
|
9
|
+
# to end with a pointer to the wire. Route it with
|
|
10
|
+
# `devise_for :users, controllers: { sessions: "kiosk/user_identity_providers/devise/sessions" }`.
|
|
11
|
+
class SessionsController < ::Devise::SessionsController
|
|
12
|
+
include WireSignpost
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def respond_to_on_destroy(non_navigational_status: :no_content)
|
|
17
|
+
return super unless non_navigational_status == :unauthorized && kiosk_json_request?
|
|
18
|
+
|
|
19
|
+
render_wire_signpost :unauthorized, 'not_signed_in',
|
|
20
|
+
message: 'there is no human web session to end — /users/sign_out closes a ' \
|
|
21
|
+
'browser session on this site, not a Kiosk credential',
|
|
22
|
+
hint: 'assistant credentials live on the wire and are dropped there: GET ' \
|
|
23
|
+
"#{request.base_url}/.well-known/kiosk.json for the register/login " \
|
|
24
|
+
'and revoke endpoints'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/concern'
|
|
4
|
+
require 'kiosk/user_identity_providers/devise'
|
|
5
|
+
|
|
6
|
+
module Kiosk
|
|
7
|
+
module UserIdentityProviders
|
|
8
|
+
class Devise
|
|
9
|
+
# Answers a JSON caller at a human page with a pointer to the Kiosk wire
|
|
10
|
+
# instead of an empty error. Include it in ApplicationController.
|
|
11
|
+
module WireSignpost
|
|
12
|
+
extend ActiveSupport::Concern
|
|
13
|
+
|
|
14
|
+
included do
|
|
15
|
+
rescue_from ActionController::InvalidAuthenticityToken do |error|
|
|
16
|
+
raise error unless kiosk_json_request?
|
|
17
|
+
|
|
18
|
+
render_wire_signpost :unprocessable_entity, 'invalid_authenticity_token',
|
|
19
|
+
message: 'this is the human sign-in page, not the Kiosk wire — it needs a ' \
|
|
20
|
+
'browser session and a CSRF token from its own form',
|
|
21
|
+
hint: 'assistants authenticate with their own keypair: GET ' \
|
|
22
|
+
"#{request.base_url}/.well-known/kiosk.json for the register/login " \
|
|
23
|
+
'endpoints, the catalog link and the modules this origin serves'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def kiosk_json_request?
|
|
30
|
+
request.format.json? || !!request.content_mime_type&.json?
|
|
31
|
+
rescue StandardError
|
|
32
|
+
false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def render_wire_signpost(status, code, message:, hint:)
|
|
36
|
+
render status:, json: { ok: false, error: { code:, message:, hint: } }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'kiosk'
|
|
4
|
+
|
|
5
|
+
module Kiosk
|
|
6
|
+
module UserIdentityProviders
|
|
7
|
+
# The user-IdP for a Rails origin whose humans sign in through Devise: it
|
|
8
|
+
# reads the request's Warden user, so password, magic-link and OmniAuth
|
|
9
|
+
# sign-ins all resolve, and a locked or unconfirmed user resolves to nil.
|
|
10
|
+
class Devise < Base
|
|
11
|
+
autoload :SessionsController, 'kiosk/user_identity_providers/devise/sessions_controller'
|
|
12
|
+
autoload :WireSignpost, 'kiosk/user_identity_providers/devise/wire_signpost'
|
|
13
|
+
|
|
14
|
+
# Raised when neither the user model nor the configuration names a role.
|
|
15
|
+
class ConfigurationError < StandardError; end
|
|
16
|
+
|
|
17
|
+
NO_ROLE = <<~MSG.strip
|
|
18
|
+
Cannot resolve a role for the Devise principal: \
|
|
19
|
+
`Kiosk.configuration.roles` is empty and the user model does not \
|
|
20
|
+
define `#kiosk_role`. Configure at least one role — e.g. \
|
|
21
|
+
`Kiosk.configure { |c| c.roles = %i[customer] }` — or add \
|
|
22
|
+
`def kiosk_role; :customer; end` to your user model.
|
|
23
|
+
MSG
|
|
24
|
+
|
|
25
|
+
# @param request [#env, #current_user, Hash] an ActionDispatch::Request,
|
|
26
|
+
# a controller exposing `#current_user`, or a Rack env Hash
|
|
27
|
+
# @return [Kiosk::Identity, nil] nil when no user is signed in
|
|
28
|
+
def verify(request)
|
|
29
|
+
user = current_user_from(request)
|
|
30
|
+
return nil if user.nil?
|
|
31
|
+
|
|
32
|
+
Kiosk::Identity.new(
|
|
33
|
+
user_id: user.public_send(Kiosk.configuration.user_id_column),
|
|
34
|
+
role: role_for(user),
|
|
35
|
+
actor: 'human',
|
|
36
|
+
agent_id: nil,
|
|
37
|
+
claims: {}
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def current_user_from(request)
|
|
44
|
+
return request.current_user if request.respond_to?(:current_user)
|
|
45
|
+
|
|
46
|
+
env = rack_env_for(request)
|
|
47
|
+
return nil if env.nil?
|
|
48
|
+
|
|
49
|
+
warden = env['warden']
|
|
50
|
+
warden&.user
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def rack_env_for(request)
|
|
54
|
+
return request.env if request.respond_to?(:env)
|
|
55
|
+
return request if request.is_a?(Hash)
|
|
56
|
+
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The model's `#kiosk_role` verbatim, nil included; else the first configured role.
|
|
61
|
+
def role_for(user)
|
|
62
|
+
return user.kiosk_role if user.respond_to?(:kiosk_role)
|
|
63
|
+
|
|
64
|
+
roles = Kiosk.configuration.roles
|
|
65
|
+
raise ConfigurationError, NO_ROLE if Array(roles).empty?
|
|
66
|
+
|
|
67
|
+
roles.first
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
require 'kiosk/user_identity_providers/devise/version'
|
|
74
|
+
require 'kiosk/user_identity_providers/devise/configuration_extension'
|
|
75
|
+
|
|
76
|
+
Kiosk::UserIdentityProviders::Devise::VERSION =
|
|
77
|
+
Kiosk::UserIdentityProviders::DeviseVersion::VERSION
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'uri'
|
|
6
|
+
|
|
7
|
+
module Kiosk
|
|
8
|
+
module UserIdentityProviders
|
|
9
|
+
# Signs a human in to a Devise origin through its real form and keeps the
|
|
10
|
+
# session cookie; `session: true` on a call sends that cookie, so an
|
|
11
|
+
# assistant's own Bearer calls never carry it.
|
|
12
|
+
class DeviseSession
|
|
13
|
+
# Raised when the sign-in handshake does not reach a signed-in session.
|
|
14
|
+
class SignInError < StandardError; end
|
|
15
|
+
|
|
16
|
+
attr_reader :server, :cookies
|
|
17
|
+
|
|
18
|
+
def initialize(server)
|
|
19
|
+
@server = server.to_s.sub(%r{/\z}, '')
|
|
20
|
+
@uri = URI(@server)
|
|
21
|
+
@cookies = {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Raises SignInError unless Devise answers the form with a redirect.
|
|
25
|
+
def sign_in!(email:, password:)
|
|
26
|
+
form = get_html('/users/sign_in')
|
|
27
|
+
raise SignInError, "sign-in form: #{form.code}" unless form.code.to_i == 200
|
|
28
|
+
|
|
29
|
+
res = post_form('/users/sign_in',
|
|
30
|
+
'authenticity_token' => csrf_token(form.body),
|
|
31
|
+
'user[email]' => email,
|
|
32
|
+
'user[password]' => password)
|
|
33
|
+
raise SignInError, "sign-in failed: #{res.code}" unless [302, 303].include?(res.code.to_i)
|
|
34
|
+
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# GET an HTML page over the session (cookies attached — this is the human).
|
|
39
|
+
def get_html(path)
|
|
40
|
+
req = Net::HTTP::Get.new(uri_for(path))
|
|
41
|
+
req['Cookie'] = cookie_header unless @cookies.empty?
|
|
42
|
+
request(req)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# POST a form over the session (cookies attached — this is the human).
|
|
46
|
+
def post_form(path, form, headers = {})
|
|
47
|
+
req = Net::HTTP::Post.new(uri_for(path), headers)
|
|
48
|
+
req['Cookie'] = cookie_header unless @cookies.empty?
|
|
49
|
+
req.set_form_data(form)
|
|
50
|
+
request(req)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# `session: true` in the headers sends the human's cookies.
|
|
54
|
+
def post_json(path, body, headers = {})
|
|
55
|
+
headers = headers.dup
|
|
56
|
+
session = headers.delete(:session)
|
|
57
|
+
req = Net::HTTP::Post.new(uri_for(path), { 'Content-Type' => 'application/json' }.merge(headers))
|
|
58
|
+
req['Cookie'] = cookie_header if session
|
|
59
|
+
req.body = JSON.generate(body)
|
|
60
|
+
parsed(request(req))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# `session: true` in the headers sends the human's cookies.
|
|
64
|
+
def get_json(path, params = {}, headers = {})
|
|
65
|
+
headers = headers.dup
|
|
66
|
+
session = headers.delete(:session)
|
|
67
|
+
uri = uri_for(path)
|
|
68
|
+
uri.query = URI.encode_www_form(params) unless params.empty?
|
|
69
|
+
req = Net::HTTP::Get.new(uri, headers)
|
|
70
|
+
req['Cookie'] = cookie_header if session
|
|
71
|
+
parsed(request(req))
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The CSRF token Rails embeds in a rendered form.
|
|
75
|
+
def csrf_token(html)
|
|
76
|
+
html[/name="authenticity_token" value="([^"]+)"/, 1]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def cookie_header = @cookies.map { |k, v| "#{k}=#{v}" }.join('; ')
|
|
80
|
+
|
|
81
|
+
# Sends a request built by the caller, absorbing every Set-Cookie; TLS follows the target's scheme.
|
|
82
|
+
def request(req)
|
|
83
|
+
target = req.uri || @uri
|
|
84
|
+
http = Net::HTTP.new(target.hostname, target.port)
|
|
85
|
+
http.use_ssl = target.scheme == 'https'
|
|
86
|
+
res = http.request(req)
|
|
87
|
+
Array(res.get_fields('set-cookie')).each do |line|
|
|
88
|
+
name, value = line.split(';').first.split('=', 2)
|
|
89
|
+
@cookies[name] = value
|
|
90
|
+
end
|
|
91
|
+
res
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def uri_for(path)
|
|
97
|
+
path.to_s.start_with?('http') ? URI(path) : URI("#{@server}#{path}")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def parsed(res)
|
|
101
|
+
[res.code.to_i, begin
|
|
102
|
+
JSON.parse(res.body)
|
|
103
|
+
rescue StandardError
|
|
104
|
+
{}
|
|
105
|
+
end]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kiosk-user-idp-devise
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.5.20
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Phil Pirozhkov
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: kiosk-core
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.5.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.5.0
|
|
26
|
+
description: |
|
|
27
|
+
kiosk-user-idp-devise is the opt-in user-IdP adapter for Rails
|
|
28
|
+
providers that authenticate principals through Devise. Add it to your
|
|
29
|
+
Gemfile explicitly — the kiosk-all meta-gem pulls in only kiosk-core
|
|
30
|
+
and kiosk-server, so IdP adapters are chosen per provider.
|
|
31
|
+
|
|
32
|
+
It reads the signed-in user from the request's Warden proxy
|
|
33
|
+
(`request.env["warden"].user`) passed by kiosk-server and returns a
|
|
34
|
+
{Kiosk::Identity} value object. Covers both Devise paths —
|
|
35
|
+
`database_authenticatable` and `omniauthable` — since both populate the
|
|
36
|
+
Warden user, so the adapter is agnostic to how the user logged in.
|
|
37
|
+
|
|
38
|
+
Lockable / confirmable handling comes for free: Devise's
|
|
39
|
+
`active_for_authentication?` already gates the Warden user, so a locked
|
|
40
|
+
or unconfirmed user yields no signed-in user and the request fails as
|
|
41
|
+
unauthenticated with no extra code.
|
|
42
|
+
|
|
43
|
+
No hard runtime dependency on Devise itself — the adapter only reads the
|
|
44
|
+
request's Warden user, so the provider's already-installed Devise
|
|
45
|
+
satisfies the requirement.
|
|
46
|
+
email:
|
|
47
|
+
- hello@fili.pp.ru
|
|
48
|
+
executables: []
|
|
49
|
+
extensions: []
|
|
50
|
+
extra_rdoc_files: []
|
|
51
|
+
files:
|
|
52
|
+
- CHANGELOG.md
|
|
53
|
+
- LICENSE.txt
|
|
54
|
+
- README.md
|
|
55
|
+
- lib/kiosk-user-idp-devise.rb
|
|
56
|
+
- lib/kiosk/user_identity_providers/devise.rb
|
|
57
|
+
- lib/kiosk/user_identity_providers/devise/configuration_extension.rb
|
|
58
|
+
- lib/kiosk/user_identity_providers/devise/sessions_controller.rb
|
|
59
|
+
- lib/kiosk/user_identity_providers/devise/version.rb
|
|
60
|
+
- lib/kiosk/user_identity_providers/devise/wire_signpost.rb
|
|
61
|
+
- lib/kiosk/user_identity_providers/devise_session.rb
|
|
62
|
+
homepage: https://kiosk.tech
|
|
63
|
+
licenses:
|
|
64
|
+
- Apache-2.0
|
|
65
|
+
metadata:
|
|
66
|
+
homepage_uri: https://kiosk.tech
|
|
67
|
+
source_code_uri: https://github.com/kiosk-hq/kiosk
|
|
68
|
+
changelog_uri: https://github.com/kiosk-hq/kiosk/blob/main/kiosk-user-idp-devise/CHANGELOG.md
|
|
69
|
+
bug_tracker_uri: https://github.com/kiosk-hq/kiosk/issues
|
|
70
|
+
rubygems_mfa_required: 'true'
|
|
71
|
+
rdoc_options: []
|
|
72
|
+
require_paths:
|
|
73
|
+
- lib
|
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
|
+
requirements:
|
|
76
|
+
- - ">="
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: '4.0'
|
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0'
|
|
84
|
+
requirements: []
|
|
85
|
+
rubygems_version: 4.0.3
|
|
86
|
+
specification_version: 4
|
|
87
|
+
summary: Devise user-IdP adapter for the Kiosk framework
|
|
88
|
+
test_files: []
|