forget-passwords 0.2.9 → 0.2.12
Sign up to get free protection for your applications and to get access to all the features.
- 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
|