forget-passwords 0.2.9 → 0.2.13
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 +69 -33
- data/lib/forget-passwords/version.rb +1 -1
- data/lib/forget-passwords.rb +42 -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: 4db3ce83ab06fe2e7ba9fbf453370f1182e8fc6e7f888e1edd2851e1eb4dfe1a
|
|
4
|
+
data.tar.gz: '049d12c72b928352235e422c2c737a7c7827d5284a68054ea09f50c7d3a962d0'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2d3bc2353a0b41009cb7b537a67bc58210670095949afbe21ada7221912c3d6a91c580240d56803b159a32a73c3d4e1c756f7b34c288879be61f842740c6bf20
|
|
7
|
+
data.tar.gz: 3c7771eb7709deeeca3c7617be519168d6387c619b5e0de0a61243905bb8a4f5a9a2667ab6a63ada192c962a426301e2d83400a5370dc212aaa7e2ee1f69eefb
|
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
|
|
|
@@ -497,6 +524,15 @@ email:
|
|
|
497
524
|
# additional SMTP configuration would go here, if applicable.
|
|
498
525
|
```
|
|
499
526
|
|
|
527
|
+
## Alternate Authentication Methods
|
|
528
|
+
|
|
529
|
+
It is possible to take the token in the cookie and feed it in as
|
|
530
|
+
either a `Basic` authentication password or `Bearer` token. In the
|
|
531
|
+
case of `Basic`, the username is ignored. This enables `curl` or API
|
|
532
|
+
access, or other automated things like feed readers. There is
|
|
533
|
+
currently no UI for this, but an "app password" management screen is
|
|
534
|
+
potentially on the horizon.
|
|
535
|
+
|
|
500
536
|
## Future Directions
|
|
501
537
|
|
|
502
538
|
This project began on something of a lark, with the intent to make a
|
|
@@ -534,22 +570,22 @@ of 2022-04-22), the focus can shift to keeping it that way.
|
|
|
534
570
|
### How about expanding out the templates?
|
|
535
571
|
|
|
536
572
|
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.
|
|
573
|
+
making domain-specific overrides so a single Forget Passwords daemon
|
|
574
|
+
could handle multiple domains with tailor-fit responses for each. I am
|
|
575
|
+
less sanguine about going hog-wild with the templates but I could see
|
|
576
|
+
some kind of future plug-in interface so people could use their
|
|
577
|
+
favourite flavour of templating engine.
|
|
542
578
|
|
|
543
579
|
### Reconcile with OAuth
|
|
544
580
|
|
|
545
|
-
The authentication cookie used by
|
|
581
|
+
The authentication cookie used by Forget Passwords bears a striking
|
|
546
582
|
resemblance to an [OAuth](https://oauth.net/) bearer token, such that
|
|
547
583
|
could actually _be_ (at least a proxy for) an OAuth bearer token.
|
|
548
584
|
Indeed, bearer tokens would make for an _excellent_ cleavage plane for
|
|
549
585
|
_segmented_ authentication: Method X to bearer token, then bearer
|
|
550
586
|
token to `REMOTE_USER`. This means we could have multiple
|
|
551
|
-
authentication mechanisms (
|
|
552
|
-
old password, whatever) operating in the same space at once.
|
|
587
|
+
authentication mechanisms (Forget Passwords, OAuth, X.509, Kerberos,
|
|
588
|
+
boring old password, whatever) operating in the same space at once.
|
|
553
589
|
|
|
554
590
|
### The really interesting thing is `mod_authnz_fcgi`
|
|
555
591
|
|
|
@@ -566,7 +602,7 @@ the content handler, that can be addressed directly—provided you write
|
|
|
566
602
|
your module in C. What `mod_authnz_fcgi` does is tap the
|
|
567
603
|
_authentication_ phase of Apache's request-handling loop and open it
|
|
568
604
|
up to cheap scripts written in any language that speak FastCGI. This
|
|
569
|
-
means that stand-alone modules like
|
|
605
|
+
means that stand-alone modules like Forget Passwords can be used in
|
|
570
606
|
conjunction with *any* downstream Web application framework or
|
|
571
607
|
development strategy. Some additional observations:
|
|
572
608
|
|
data/lib/forget-passwords.rb
CHANGED
|
@@ -11,6 +11,7 @@ require 'rack'
|
|
|
11
11
|
require 'rack/request'
|
|
12
12
|
require 'rack/response'
|
|
13
13
|
|
|
14
|
+
require 'base64'
|
|
14
15
|
require 'mail'
|
|
15
16
|
|
|
16
17
|
module ForgetPasswords
|
|
@@ -384,7 +385,8 @@ module ForgetPasswords
|
|
|
384
385
|
resp.set_header "Variable-#{@vars[:redirect]}", target.to_s if
|
|
385
386
|
target != uri # (note this should always be true)
|
|
386
387
|
resp.set_cookie @keys[:cookie], {
|
|
387
|
-
value: token, secure: req.ssl?, httponly: true,
|
|
388
|
+
value: token, secure: req.ssl?, httponly: true,
|
|
389
|
+
domain: uri.host, path: ?/, same_site: :strict,
|
|
388
390
|
expires: time_delta(@state.expiry[:cookie]),
|
|
389
391
|
}
|
|
390
392
|
|
|
@@ -397,12 +399,12 @@ module ForgetPasswords
|
|
|
397
399
|
resp
|
|
398
400
|
end
|
|
399
401
|
|
|
400
|
-
def
|
|
401
|
-
token ||= req.cookies[@keys[:cookie]]
|
|
402
|
-
|
|
402
|
+
def handle_token req, token, now = Time.now
|
|
403
403
|
resp = Rack::Response.new
|
|
404
404
|
|
|
405
|
-
|
|
405
|
+
uri = req_uri req
|
|
406
|
+
|
|
407
|
+
vars = { LOGIN: @targets[:login], FORWARD: uri.to_s }
|
|
406
408
|
|
|
407
409
|
# check if token is well-formed
|
|
408
410
|
raise_error(409, :cookie_bad, req, vars: vars) unless token_ok? token
|
|
@@ -416,20 +418,13 @@ module ForgetPasswords
|
|
|
416
418
|
user = @state.user_for(token, record: true, cookie: true)
|
|
417
419
|
|
|
418
420
|
raise_error(403, :email_not_listed, req, vars: vars) unless
|
|
419
|
-
@state.acl.listed?
|
|
421
|
+
@state.acl.listed? uri, user.email
|
|
420
422
|
|
|
421
|
-
now = Time.now
|
|
422
423
|
@state.freshen_token token, from: now
|
|
423
424
|
|
|
424
425
|
# stamp the token
|
|
425
426
|
@state.stamp_token token, req.ip, seen: now
|
|
426
427
|
|
|
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
428
|
# just set the variable
|
|
434
429
|
resp.set_header "Variable-#{@vars[:user]}", user.principal.to_s
|
|
435
430
|
|
|
@@ -439,6 +434,23 @@ module ForgetPasswords
|
|
|
439
434
|
resp
|
|
440
435
|
end
|
|
441
436
|
|
|
437
|
+
def handle_cookie req, token = nil
|
|
438
|
+
token ||= req.cookies[@keys[:cookie]]
|
|
439
|
+
|
|
440
|
+
now = Time.now
|
|
441
|
+
resp = handle_token req, token, now
|
|
442
|
+
uri = req_uri req
|
|
443
|
+
|
|
444
|
+
# update the cookie expiration
|
|
445
|
+
resp.set_cookie @keys[:cookie], {
|
|
446
|
+
value: token, secure: req.ssl?, httponly: true,
|
|
447
|
+
domain: uri.host, path: ?/, same_site: :strict,
|
|
448
|
+
expires: time_delta(@state.expiry[:cookie], now),
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
resp
|
|
452
|
+
end
|
|
453
|
+
|
|
442
454
|
def handle_login req
|
|
443
455
|
uri = req_uri req
|
|
444
456
|
resp = Rack::Response.new
|
|
@@ -519,23 +531,24 @@ module ForgetPasswords
|
|
|
519
531
|
resp
|
|
520
532
|
end
|
|
521
533
|
|
|
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
534
|
def handle_auth req
|
|
538
|
-
|
|
535
|
+
auth = req.get_header('Authorization') || req.env['HTTP_AUTHORIZATION']
|
|
536
|
+
if auth and !auth.strip.empty?
|
|
537
|
+
mech, *auth = auth.strip.split
|
|
538
|
+
token = case mech.downcase
|
|
539
|
+
when 'basic'
|
|
540
|
+
# can't trust/use rack here
|
|
541
|
+
Base64.decode64(auth.first || '').split(?:, 2).last
|
|
542
|
+
when 'bearer'
|
|
543
|
+
auth.first
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
if token
|
|
547
|
+
handle_token req, token
|
|
548
|
+
else
|
|
549
|
+
default_401 req
|
|
550
|
+
end
|
|
551
|
+
elsif knock = req.GET[@keys[:query]]
|
|
539
552
|
# check for a knock first; this overrides everything
|
|
540
553
|
handle_knock req, knock
|
|
541
554
|
# 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.13
|
|
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-08 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
|