forget-passwords 0.2.9 → 0.2.12
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 +60 -33
- data/lib/forget-passwords/version.rb +1 -1
- data/lib/forget-passwords.rb +38 -29
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3215dc2df30a7f53896530fe46e576ccc68e69a9bdeb9dfdcc1d8d55f482077
|
4
|
+
data.tar.gz: 56479273ba73c937bda4449b3a9ebbae2aa41736cf8e01bc58c3c209f3179b51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c95ee03bc4b33e7bce7ef05ea11633defac41664eecec042b0ff15ccdd2ef5dbd95ad3faa9336e9f2dcbd583adf0db4cf1674a585007027a31c73d3a732e9b9
|
7
|
+
data.tar.gz: 4f98e7c9feb6fd09470c93f820ff5aca5bdfaa27a4e845e6a8efb484cde2ff319224cb3a18435c805ac4998f0ad1f6975d22c8ccb813e76343beadc981625de3
|
data/README.md
CHANGED
@@ -95,10 +95,11 @@ module to operate.
|
|
95
95
|
|
96
96
|
## Usage
|
97
97
|
|
98
|
-
To start using
|
99
|
-
setup on the server (below), as well as all the necessary
|
100
|
-
address to send e-mail from. After that, we'll need a
|
101
|
-
example uses PostgreSQL; you can of course skip this
|
98
|
+
To start using Forget Passwords, we'll assume you have done the
|
99
|
+
necessary setup on the server (below), as well as all the necessary
|
100
|
+
setup for an address to send e-mail from. After that, we'll need a
|
101
|
+
database (this example uses PostgreSQL; you can of course skip this
|
102
|
+
step for SQLite):
|
102
103
|
|
103
104
|
$ createdb forgetpw
|
104
105
|
|
@@ -134,8 +135,8 @@ principle it is usable in other systems. What follows is the
|
|
134
135
|
configuration for Apache 2.4.x or newer.
|
135
136
|
|
136
137
|
First, we need to declare the authenticator (here it can be called
|
137
|
-
anything but we are appropriately calling it `ForgetPasswords`) and
|
138
|
-
it's listening:
|
138
|
+
anything but we are appropriately calling it `ForgetPasswords`) and
|
139
|
+
where it's listening:
|
139
140
|
|
140
141
|
```apache
|
141
142
|
AuthnzFcgiDefineProvider authn ForgetPasswords fcgi://localhost:10101/
|
@@ -155,7 +156,7 @@ throwaway user like `nobody`, and then subsequently deny that user. (I
|
|
155
156
|
would consider this a design flaw in `mod_authnz_fcgi`.) The
|
156
157
|
expression `%{reqenv:FCGI_USER}` (where the slug `FCGI_USER` is
|
157
158
|
configurable on our side) is how the identity gets transmitted
|
158
|
-
upstream from
|
159
|
+
upstream from Forget Passwords to the server.
|
159
160
|
|
160
161
|
```apache
|
161
162
|
<Location /protected>
|
@@ -174,7 +175,7 @@ Another idiosyncrasy of `mod_authnz_fcgi` is that while it uses the
|
|
174
175
|
to the client necessarily has to come from the downstram content
|
175
176
|
handler. As such, any other information from a _successful_
|
176
177
|
authentication response needs to be smuggled out through environment
|
177
|
-
variables. Since
|
178
|
+
variables. Since Forget Passwords performs a redirect to remove the
|
178
179
|
authentication token from the URL upon successful authentication, the
|
179
180
|
following `mod_rewrite` configuration needs to be in place to turn the
|
180
181
|
environment variable back into an actual redirect:
|
@@ -224,17 +225,42 @@ ProxyPass /logout fcgi://localhost:10101/logout
|
|
224
225
|
> crash with a protocol error. While the handling is less than
|
225
226
|
> delicate, this is actually a reasonable expectation, as request
|
226
227
|
> bodies are only read once off the wire and will thus be already
|
227
|
-
> consumed (whether or not they contain the fields to which
|
228
|
-
> is sensitive) when the content handler is invoked. (The
|
229
|
-
> handles the request body, it _can_ be duplicated and
|
230
|
-
> the input stream, but that is a whole project unto
|
228
|
+
> consumed (whether or not they contain the fields to which Forget
|
229
|
+
> Passwords is sensitive) when the content handler is invoked. (The
|
230
|
+
> way Apache handles the request body, it _can_ be duplicated and
|
231
|
+
> reinserted into the input stream, but that is a whole project unto
|
232
|
+
> itself.
|
233
|
+
|
234
|
+
### Caveats
|
235
|
+
|
236
|
+
I have noticed that a `RewriteRule` (in a `.htaccess`) with the
|
237
|
+
passthrough (`PT`) flag will short-circuit the redirect that happens
|
238
|
+
when a user follows the link off an e-mail. Same ostensibly goes for
|
239
|
+
overriding `DirectoryIndex` in a `.htaccess`. The observable effect is
|
240
|
+
that the server returns 401 (and doesn't redirect/remove the query
|
241
|
+
string) even though the cookie is set and the knock token is consumed.
|
242
|
+
If you refresh the page, then it will say (correctly, from its point
|
243
|
+
of view) that the link is expired. If you manually chop off the query
|
244
|
+
string, it will correctly display the logged-in state.
|
245
|
+
|
246
|
+
> One thing I didn't check is if it still returned a `Location:`
|
247
|
+
> header, which the browser will ignore if the response code is
|
248
|
+
> anything other than most (but not all) of the 300s and 201.
|
249
|
+
|
250
|
+
This is likely because these configuration directives are causing
|
251
|
+
subrequests and/or internal redirects, which triggers the handler, but
|
252
|
+
doesn't convey its response to the client. This might be an inherent
|
253
|
+
limitation of using FastCGI in `AUTHORIZER` mode, because there is no
|
254
|
+
way to tell it that it is being triggered from a subrequest (unless
|
255
|
+
there is?). More research is needed to probe potential interactions
|
256
|
+
with other handlers.
|
231
257
|
|
232
258
|
## Templates
|
233
259
|
|
234
|
-
|
235
|
-
take the form of template files. The functionality of these
|
236
|
-
is currently at the absolute bare minimum required to do the
|
237
|
-
templates are XHTML, with a basic placeholder substitution
|
260
|
+
Forget Passwords has a number of UI states that are embedded in the
|
261
|
+
gem. These take the form of template files. The functionality of these
|
262
|
+
templates is currently at the absolute bare minimum required to do the
|
263
|
+
job. The templates are XHTML, with a basic placeholder substitution
|
238
264
|
functionality, which can take place either in processing instructions
|
239
265
|
(`<?var $WHATEVER?>`), or attribute values (`<elem
|
240
266
|
attr="$WHATEVER"/>`).
|
@@ -303,7 +329,7 @@ form field by the name of `forward` that contains the current URL.
|
|
303
329
|
|
304
330
|
This resource should actually never be seen, as it currently only
|
305
331
|
arises when outside content-handling traffic is directed to locations
|
306
|
-
other than the two specified by
|
332
|
+
other than the two specified by Forget Passwords.
|
307
333
|
|
308
334
|
### `knock_bad` (currently handled by `basic-409.xhtml`)
|
309
335
|
|
@@ -378,8 +404,8 @@ script can't connectd to the specified SMTP server.
|
|
378
404
|
|
379
405
|
### `email_sent` (currently handled by `email-sent.xhtml`)
|
380
406
|
|
381
|
-
This is the confirmation page people see when
|
382
|
-
their e-mmail address and sent the link-containing e-mail.
|
407
|
+
This is the confirmation page people see when Forget Passwords has
|
408
|
+
accepted their e-mmail address and sent the link-containing e-mail.
|
383
409
|
|
384
410
|
### `post_only` (currently handled by `post-405.xhtml`)
|
385
411
|
|
@@ -450,14 +476,15 @@ specific functions within the system, and have a stable location.
|
|
450
476
|
|
451
477
|
* `login` is the target that accepts the `POST` request from the `401`
|
452
478
|
page and others, that sends the e-mail and issues a confirmation. It
|
453
|
-
defaults to `/email-link`. This resource is powered by
|
454
|
-
is used internally to configure the location of that
|
479
|
+
defaults to `/email-link`. This resource is powered by Forget
|
480
|
+
Passwords and is used internally to configure the location of that
|
481
|
+
resource.
|
455
482
|
* `logout` is the target that accepts the `POST` request to log
|
456
483
|
out. It (rather predictably) defaults to `/logout`. This location is
|
457
|
-
also handled by
|
484
|
+
also handled by Forget Passwords.
|
458
485
|
* `logout_one` is a _static_ (or other arbitrary) target (i.e., _not_
|
459
|
-
handled by
|
460
|
-
current session. It defaults to `/logged-out`.
|
486
|
+
handled by Forget Passwords) that confirms the user has logged out
|
487
|
+
their current session. It defaults to `/logged-out`.
|
461
488
|
* `logout_all` is another static target that confirms the user has
|
462
489
|
logged out of all devices.
|
463
490
|
|
@@ -534,22 +561,22 @@ of 2022-04-22), the focus can shift to keeping it that way.
|
|
534
561
|
### How about expanding out the templates?
|
535
562
|
|
536
563
|
Localizing the templates is definitely a possibility, as well as
|
537
|
-
making domain-specific overrides so a single
|
538
|
-
handle multiple domains with tailor-fit responses for each. I am
|
539
|
-
sanguine about going hog-wild with the templates but I could see
|
540
|
-
kind of future plug-in interface so people could use their
|
541
|
-
flavour of templating engine.
|
564
|
+
making domain-specific overrides so a single Forget Passwords daemon
|
565
|
+
could handle multiple domains with tailor-fit responses for each. I am
|
566
|
+
less sanguine about going hog-wild with the templates but I could see
|
567
|
+
some kind of future plug-in interface so people could use their
|
568
|
+
favourite flavour of templating engine.
|
542
569
|
|
543
570
|
### Reconcile with OAuth
|
544
571
|
|
545
|
-
The authentication cookie used by
|
572
|
+
The authentication cookie used by Forget Passwords bears a striking
|
546
573
|
resemblance to an [OAuth](https://oauth.net/) bearer token, such that
|
547
574
|
could actually _be_ (at least a proxy for) an OAuth bearer token.
|
548
575
|
Indeed, bearer tokens would make for an _excellent_ cleavage plane for
|
549
576
|
_segmented_ authentication: Method X to bearer token, then bearer
|
550
577
|
token to `REMOTE_USER`. This means we could have multiple
|
551
|
-
authentication mechanisms (
|
552
|
-
old password, whatever) operating in the same space at once.
|
578
|
+
authentication mechanisms (Forget Passwords, OAuth, X.509, Kerberos,
|
579
|
+
boring old password, whatever) operating in the same space at once.
|
553
580
|
|
554
581
|
### The really interesting thing is `mod_authnz_fcgi`
|
555
582
|
|
@@ -566,7 +593,7 @@ the content handler, that can be addressed directly—provided you write
|
|
566
593
|
your module in C. What `mod_authnz_fcgi` does is tap the
|
567
594
|
_authentication_ phase of Apache's request-handling loop and open it
|
568
595
|
up to cheap scripts written in any language that speak FastCGI. This
|
569
|
-
means that stand-alone modules like
|
596
|
+
means that stand-alone modules like Forget Passwords can be used in
|
570
597
|
conjunction with *any* downstream Web application framework or
|
571
598
|
development strategy. Some additional observations:
|
572
599
|
|
data/lib/forget-passwords.rb
CHANGED
@@ -384,7 +384,8 @@ module ForgetPasswords
|
|
384
384
|
resp.set_header "Variable-#{@vars[:redirect]}", target.to_s if
|
385
385
|
target != uri # (note this should always be true)
|
386
386
|
resp.set_cookie @keys[:cookie], {
|
387
|
-
value: token, secure: req.ssl?, httponly: true,
|
387
|
+
value: token, secure: req.ssl?, httponly: true,
|
388
|
+
domain: uri.host, path: ?/, same_site: :strict,
|
388
389
|
expires: time_delta(@state.expiry[:cookie]),
|
389
390
|
}
|
390
391
|
|
@@ -397,12 +398,12 @@ module ForgetPasswords
|
|
397
398
|
resp
|
398
399
|
end
|
399
400
|
|
400
|
-
def
|
401
|
-
token ||= req.cookies[@keys[:cookie]]
|
402
|
-
|
401
|
+
def handle_token req, token, now = Time.now
|
403
402
|
resp = Rack::Response.new
|
404
403
|
|
405
|
-
|
404
|
+
uri = req_uri req
|
405
|
+
|
406
|
+
vars = { LOGIN: @targets[:login], FORWARD: uri.to_s }
|
406
407
|
|
407
408
|
# check if token is well-formed
|
408
409
|
raise_error(409, :cookie_bad, req, vars: vars) unless token_ok? token
|
@@ -416,20 +417,13 @@ module ForgetPasswords
|
|
416
417
|
user = @state.user_for(token, record: true, cookie: true)
|
417
418
|
|
418
419
|
raise_error(403, :email_not_listed, req, vars: vars) unless
|
419
|
-
@state.acl.listed?
|
420
|
+
@state.acl.listed? uri, user.email
|
420
421
|
|
421
|
-
now = Time.now
|
422
422
|
@state.freshen_token token, from: now
|
423
423
|
|
424
424
|
# stamp the token
|
425
425
|
@state.stamp_token token, req.ip, seen: now
|
426
426
|
|
427
|
-
# update the cookie expiration
|
428
|
-
resp.set_cookie @keys[:cookie], {
|
429
|
-
value: token, secure: req.ssl?, httponly: true,
|
430
|
-
expires: time_delta(@state.expiry[:cookie], now),
|
431
|
-
}
|
432
|
-
|
433
427
|
# just set the variable
|
434
428
|
resp.set_header "Variable-#{@vars[:user]}", user.principal.to_s
|
435
429
|
|
@@ -439,6 +433,23 @@ module ForgetPasswords
|
|
439
433
|
resp
|
440
434
|
end
|
441
435
|
|
436
|
+
def handle_cookie req, token = nil
|
437
|
+
token ||= req.cookies[@keys[:cookie]]
|
438
|
+
|
439
|
+
now = Time.now
|
440
|
+
resp = handle_token req, token, now
|
441
|
+
uri = req_uri req
|
442
|
+
|
443
|
+
# update the cookie expiration
|
444
|
+
resp.set_cookie @keys[:cookie], {
|
445
|
+
value: token, secure: req.ssl?, httponly: true,
|
446
|
+
domain: uri.host, path: ?/, same_site: :strict,
|
447
|
+
expires: time_delta(@state.expiry[:cookie], now),
|
448
|
+
}
|
449
|
+
|
450
|
+
resp
|
451
|
+
end
|
452
|
+
|
442
453
|
def handle_login req
|
443
454
|
uri = req_uri req
|
444
455
|
resp = Rack::Response.new
|
@@ -519,23 +530,21 @@ module ForgetPasswords
|
|
519
530
|
resp
|
520
531
|
end
|
521
532
|
|
522
|
-
# def handle_post req
|
523
|
-
# return Rack::Response[403, { 'Content-Type' => 'text/plain' }, 'wat lol']
|
524
|
-
|
525
|
-
# if logout = req.POST[@keys[:logout]]
|
526
|
-
# handle_logout req, logout
|
527
|
-
# elsif email = req.POST[@keys[:email]]
|
528
|
-
# handle_login req, email
|
529
|
-
# elsif token = req.cookies[@keys[:cookie]]
|
530
|
-
# # next check for a cookie
|
531
|
-
# handle_cookie req, token
|
532
|
-
# else
|
533
|
-
# default_401 req
|
534
|
-
# end
|
535
|
-
# end
|
536
|
-
|
537
533
|
def handle_auth req
|
538
|
-
if
|
534
|
+
if auth = req.get_header('Authorization')
|
535
|
+
token = if req.basic?
|
536
|
+
# auto-decodes (XXX do we care about the username??)
|
537
|
+
req.credentials.last
|
538
|
+
elsif auth.strip.downcase.start_with? 'bearer'
|
539
|
+
auth.strip.split[1]
|
540
|
+
end
|
541
|
+
if token
|
542
|
+
handle_token req, token
|
543
|
+
else
|
544
|
+
# XXX one day maybe this can be more descriptive??
|
545
|
+
default_401 req
|
546
|
+
end
|
547
|
+
elsif knock = req.GET[@keys[:query]]
|
539
548
|
# check for a knock first; this overrides everything
|
540
549
|
handle_knock req, knock
|
541
550
|
# elsif req.post?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forget-passwords
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dorian Taylor
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -305,7 +305,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
305
305
|
- !ruby/object:Gem::Version
|
306
306
|
version: '0'
|
307
307
|
requirements: []
|
308
|
-
rubygems_version: 3.3.
|
308
|
+
rubygems_version: 3.3.11
|
309
309
|
signing_key:
|
310
310
|
specification_version: 4
|
311
311
|
summary: Web authentication module for the extremely lazy
|