Hi All,

(This was a shower thought that I wanted to write down while it was in
my head - comments welcome, but no action required.)

In previous discussions of adding two-factor auth to FxA, we've
struggled with the issue of backwards-compatibility for API consumers
that don't know how to do 2FA.

The standard solution here is to let the user generate one-time-use
"application passwords" that can be entered into legacy systems.  For
example, this is how you can login to gmail in thunderbird if you have
2FA enabled:

  https://support.google.com/accounts/answer/185833?hl=en

Things aren't so simple for us, because the password in FxA does
double-duty as a source of entropy for your encryption key.  I couldn't
find any historical docs on a solution for this, and I recall some folks
saying that we hadn't found a good solution.

So apologies if we've already tread this ground before :-)

But I think we could in fact make it work - the web app could generate
application passwords by doing the same crypto steps as the
change-password flow, but submitting the results to a different API
endpoint so they're saved as an extra password, rather than replacing
the existing password.

Here's a strawman modeled off the change-password API:


1) Provide the current password, and get a keyfetch token:

  > POST /v1/password/appkey/start
  > {
  >    "email": "[email protected]",
  >    "authPW": "hashofcurrentpassword"
  > }
  .
  < {
  <   "keyFetchToken": "token"
  < }

2) Use the keyfetchtoken to obtain current value of kB using
   the existing API

3) Generate a random hex string.  This will be "appPW", the
   application password that is ultimately given to the user.

4) Do the pbkdf-ing and hashing necessary to calculate authPW
   from this key, and to wrap kB with it:

     quickStretchedPW = PBKDF(appPW, email)
     authPW = HKDF(quickStretchedPW, "authPW")
     unwrapBkey = HKDF(quickStretchedPW, "unwrapBkey")
     wrapKb = XOR(kB, unwrapBkey)

5) Submit these values to the server to register a new application
   password:


  > POST /v1/password/appkey/finish
  > Authorization: session token palaver here
  > {
  >    "email": "[email protected]",
  >    "authPW": "authPW calculated from the appPW",
  >    "wrapKb": "wrapKb calculated from the appPW"
  > }
  .
  < { }

6) The server stores these in a separate table.  When processing a
   login attempt, it first tries the password from the main accounts
   table, then tries any application passwords found in this additional
   table.

7) Present the appPW to the user to copy-paste into their legacy app.

>From the perspective of a legacy device, you can enter the "appPW"
instead of your account password, and it'd behave just like you entered
a regular valid password.

The one thing that we *couldn't* do with this flow, is store the
application password in plaintext in the db, since it's equivalent in
power to kB.  The user would have to write it down somewhere at the time
of generation, or it would be lost forever.


Thoughts?


  Cheers,

    Ryan
_______________________________________________
Dev-fxacct mailing list
[email protected]
https://mail.mozilla.org/listinfo/dev-fxacct

Reply via email to