Skip to content
This repository was archived by the owner on Apr 3, 2019. It is now read-only.

Add ability to verify login with token code#2218

Merged
vbudhram merged 3 commits intomasterfrom
feature.signincode
Dec 20, 2017
Merged

Add ability to verify login with token code#2218
vbudhram merged 3 commits intomasterfrom
feature.signincode

Conversation

@vbudhram
Copy link
Contributor

@vbudhram vbudhram commented Nov 12, 2017

This exposes the ability for a client to specify how it wants to verify the login.

  • Add tests
  • Cleanup code and add docs
  • Update to handle A/B tests

@vbudhram vbudhram added the WIP label Nov 12, 2017
@vbudhram vbudhram self-assigned this Nov 12, 2017
@ghost ghost added the waffle:active label Nov 12, 2017
vladikoff pushed a commit to mozilla/fxa-auth-db-mysql that referenced this pull request Nov 27, 2017
…dikoff,@rfk

This PR exposes new APIs to create and verify sessionTokens using a short code. Couple notes

- Each code has an expiration date
- Codes are stored as hashes in database
- Support for resending (aka regenerating) a code to come in later PR.
- New endpoint `verifyTokensShortCode` used to explicitly verify shortCodes. 

Connects with mozilla/fxa-auth-server#2218

Feature doc: https://docs.google.com/document/d/1xQtCGBJ9XZuF1q9faZV3YIiOnxomQMHKiTP62YSf3v8/edit
@vbudhram vbudhram force-pushed the feature.signincode branch 2 times, most recently from dc51a86 to cb68dcf Compare November 30, 2017 22:03
@vbudhram vbudhram changed the title Add ability to verify login with sign-in code Add ability to verify login with token code Nov 30, 2017
@vbudhram vbudhram force-pushed the feature.signincode branch 2 times, most recently from 73323b6 to da3798d Compare December 6, 2017 18:07
@vbudhram
Copy link
Contributor Author

vbudhram commented Dec 6, 2017

@mozilla/fxa-devs I think this is ready, r?

Copy link
Contributor

@rfk rfk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good stuff @vbudhram! I left a bunch of smaller comments, but I also have two larger ones.

First, can you please generate a screenshot of two of the resulting emails for @ryanfeeley's explicit review?

Second, I don't see anything in here about re-sending the email-2fa email, but the design for the front-end in [1] does include a "resend" link. Do we need a new endpoint to trigger that resend? (And some additional security checks to ensure an attacker doesn't try to use it for old sessions that don't have a short-code attached?).

[1] https://docs.google.com/document/d/1xQtCGBJ9XZuF1q9faZV3YIiOnxomQMHKiTP62YSf3v8/edit?ts=5a0f23d8#heading=h.n7eo6t754fco

config/dev.json Outdated
"signinConfirmation": {
"skipForNewAccounts": {
"enabled": false
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary to e.g. get the tests to pass properly, or was it for local dev?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for local dev, but I don't think it is needed.

format: 'duration',
default: '1 hour',
env: 'SIGNIN_TOKEN_CODE_LIFETIME'
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my own sense of thoroughness, noting that these are the same parameters used by the similarly-powerful "unblock codes" feature 👍

lib/db.js Outdated
'/tokens/' + code + '/verifyCode',
{ uid: accountData.uid }
)
.then(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the indentation of .then( here is mis-aligned with the paren on the line above, which doesn't match the style of other code in this file.

},
details
)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just flagging that we'll need to add support in fxa-content-server for handling these new codes.


const tokenCodeConfig = config.signinConfirmation.tokenVerificationCode
const tokenCodeLifetime = tokenCodeConfig && tokenCodeConfig.codeLifetime || 0
const tokenCodeLength = tokenCodeConfig && tokenCodeConfig.codeLength || 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zero doesn't seem like a safe fallback value for the code length here. A missing length should either disable the feature entirely, or throw as a hard config error of some sort.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use default value of 1 hour.

path: '/tokenCodes/verify',
config: {
validate: {
payload: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests suggest this should take a metricsContext payload param as well, but it doesn't appear to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the metricsContext requirement and emit metrics using request.emit

console.log('\x1B[32m', link, '\x1B[39m')
}
else if (sc) {
console.log('\x1B[32mSign-in code: ', sc, '\x1B[39m')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, this may be a little confusing because "signin code" is already the name of something else. Could we call this e.g. "verification code" or similar?

assert.equal(status.emailVerified, true, 'email is verified')
assert.equal(status.sessionVerified, false, 'session is not verified')
})
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this also assert that the expected email was sent, like the previous test does?

const metricsContext = {
flowId: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
flowBeginTime: Date.now()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ISTM the tests should assert something about this metricsContext, e.g. that the correct flow-id header was present in the outgoing email.

})
})

it('should consume valid code', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For completeness, can you please add a remote test to assert that invalid token codes are rejected, and leave the session unverified? It'll be good to double-check that end-to-end.

@vbudhram
Copy link
Contributor Author

@rfk Updated! Opted to use /session/verify/token route since I think it allows us to extend to other methods a little more clearly. r?

Copy link
Contributor

@rfk rfk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r+ with nits, thanks @vbudhram

I left a couple of small nits, but also realized one broader concern w.r.t metrics. IIUC users who end up in the treatment group for this experiment would no longer emit an account.confirmed metrics event. I suspect that they should, and that we should just add one in the /session/verify/token endpoint. But it's possible I don't understand all the nuance involved. Thoughts?


const tokenCodeConfig = config.signinConfirmation.tokenVerificationCode
const tokenCodeLifetime = tokenCodeConfig && tokenCodeConfig.codeLifetime || MS_ONE_HOUR
const tokenCodeLength = tokenCodeConfig && tokenCodeConfig.codeLength || 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, you changed this to make the default for tokenCodeLifetime be 1 hour, but by concern was about the default for tokenCodeLength - we don't want a configuration error to accidentally make "" a valid verification code for all sessions. It may also be worth changing the defaults for unblockCode* above to match whatever you choose here, for consistency.

const uid = request.payload.uid
const code = request.payload.code.toUpperCase()

customs.checkIpOnly(request, 'verifyTokenCode')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we've authenticated with the sessionToken here, we have the user's email address available in request.auth.credentials.email, meaning it could use customs.check here rather than customs.checkIpOnly. It's probably worth doing so, to avoid incorrectly rate-limited people who happen to be behind a shared IP.


return request.emitMetricsEvent('tokenCodes.verified', {uid: uid})
.then(() => ({}))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect we will need to emit an account.confirmed metrics event here, for at least two reasons:

  • This event is used by amplitude to generate fxa_login - email_confirmed events, so users in this experiment might otherwise disappear from our login funnels in amplitude
  • This is used as the flow-complete signal for non-sync logins, so users in this experiment might incorrectly appear to have failed to complete their login flow.

return Client.login(config.publicUrl, email, password, {
verificationMethod: 'email-2fa'
})
.then((res) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: weird indentation here?

return Client.login(config.publicUrl, email, password, {
verificationMethod: 'email-2fa'
})
.then((res) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: weird indentation here?

@vbudhram vbudhram merged commit 677bdbb into master Dec 20, 2017
@vbudhram
Copy link
Contributor Author

@rfk Thank you! Added updated metrics

@vbudhram vbudhram deleted the feature.signincode branch February 22, 2018 16:32
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants