Skip to content

Add phone verification #12258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 29, 2022
Merged

Conversation

svenefftinge
Copy link
Member

@svenefftinge svenefftinge commented Aug 22, 2022

Description

This PR adds phone number verification before untrusted users can start a workspace.

A user is considered trusted if

  • Their GitHub account is older than 30 days, or
  • Their GitLab account confirmed_at event is older than 30 days, or
  • Their Gitpod account has been created before 2022-08-24 (i.e. existing users)

Related Issue(s)

Fixes #11339

See also https://github.com/gitpod-io/ops/pull/4149

How to test

Test 1

  • Create a fresh GitHub account and sign in (use admin to unblock).
  • You should need to do phone number verification before you can start your first workspace.
  • Once the number is verified you should be able to work as usual.

Test 2

  • Use a GitHub account older than 30 days.
  • You should be able to start a workspace without verification.

Test 1 ++

  • delete the lastVerificationTimestamp in your user's additional Data.
  • if the account had started a workspace before, you should not need to verify
  • if the account doesn't have any workspaces, you should be asked to verify

Release Notes

Added phone number verification in SaaS, to help mitigate abuse.

Documentation

Werft options:

  • /werft with-preview

@svenefftinge svenefftinge requested review from a team August 22, 2022 09:14
@svenefftinge svenefftinge marked this pull request as draft August 22, 2022 09:14
@github-actions github-actions bot added team: delivery Issue belongs to the self-hosted team team: webapp Issue belongs to the WebApp team labels Aug 22, 2022
@svenefftinge
Copy link
Member Author

/werft run with-clean-slate-deployment=true

@svenefftinge svenefftinge force-pushed the sefftinge/add-phone-verification-11339 branch 2 times, most recently from a603f6a to 7908ff0 Compare August 22, 2022 09:53
@svenefftinge
Copy link
Member Author

svenefftinge commented Aug 22, 2022

/werft run with-clean-slate-deployment=true

👍 started the job as gitpod-build-sefftinge-add-phone-verification-11339.22
(with .werft/ from main)

@svenefftinge svenefftinge force-pushed the sefftinge/add-phone-verification-11339 branch 3 times, most recently from 971d89a to a87f052 Compare August 23, 2022 10:41
Copy link
Contributor

@gtsiolis gtsiolis left a comment

Choose a reason for hiding this comment

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

Left some early feedback on the UX changes below. 🥯

visible={true}
>
<Alert type="warning" className="mt-2">
To use Gitpod you'll need to validate your account with your phone number. This is required to
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: Does it help us achieve our goal (abuse)? I'd rather go with a neutral tone but any option could suffice. Feel free to ignore this nitpick. 🙂

Suggested change
To use Gitpod you'll need to validate your account with your phone number. This is required to
To use Gitpod you'll need to validate your account with a mobile phone number. This is required to

@svenefftinge svenefftinge force-pushed the sefftinge/add-phone-verification-11339 branch 2 times, most recently from 0924951 to 6ecdbfa Compare August 23, 2022 14:03
@gtsiolis
Copy link
Contributor

gtsiolis commented Aug 23, 2022

Looking at this now! 👀

Copy link
Contributor

@gtsiolis gtsiolis left a comment

Choose a reason for hiding this comment

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

@svenefftinge I've left another round of comments. 🥊

I could not go through all the steps of the flow. 😭

@svenefftinge svenefftinge force-pushed the sefftinge/add-phone-verification-11339 branch from 6ecdbfa to a3bcdf3 Compare August 24, 2022 08:44
@gtsiolis
Copy link
Contributor

gtsiolis commented Aug 24, 2022

Looking at this now! 👀

Copy link
Contributor

@gtsiolis gtsiolis left a comment

Choose a reason for hiding this comment

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

Alright! Left a few general comments and questions below. 🔮

Most of them can be ignored or used for opening follow up issues out of the scope of these changes. 🛹

Thanks @svenefftinge for pushing this forward! 🏀

visible={true}
>
<Alert type="message" className="mt-2">
Your account has been successfully verified.
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Now that we've tried this, we probably need a new alert component variant for successful messages like this that is using green-ish colors. Could be fine to leave this out of the scope of these changes for now. Your call.

Alert / Info (IMPLEMENTED) Alert / Success (NOT IMPLEMENTED) Alert / Success (NOT IMPLEMENTED) 🌔
Frame 381 Frame 382 Frame 385

If you'd like to add this here, here's the icon (SVG) and the colors used for light and dark themes:

LIGHT THEME ⛅

Background: green-100
Icon: green-500
Text: green-700

<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 18C14.4183 18 18 14.4183 18 10C18 5.58172 14.4183 2 10 2C5.58172 2 2 5.58172 2 10C2 14.4183 5.58172 18 10 18ZM13.7071 8.70711C14.0976 8.31658 14.0976 7.68342 13.7071 7.29289C13.3166 6.90237 12.6834 6.90237 12.2929 7.29289L9 10.5858L7.70711 9.29289C7.31658 8.90237 6.68342 8.90237 6.29289 9.29289C5.90237 9.68342 5.90237 10.3166 6.29289 10.7071L8.29289 12.7071C8.68342 13.0976 9.31658 13.0976 9.70711 12.7071L13.7071 8.70711Z" fill="#84CC16"/>
</svg>

DARK THEME 🌔

Background: green-800
Icon: green-50
Text: green-100

<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 18C14.4183 18 18 14.4183 18 10C18 5.58172 14.4183 2 10 2C5.58172 2 2 5.58172 2 10C2 14.4183 5.58172 18 10 18ZM13.7071 8.70711C14.0976 8.31658 14.0976 7.68342 13.7071 7.29289C13.3166 6.90237 12.6834 6.90237 12.2929 7.29289L9 10.5858L7.70711 9.29289C7.31658 8.90237 6.68342 8.90237 6.29289 9.29289C5.90237 9.68342 5.90237 10.3166 6.29289 10.7071L8.29289 12.7071C8.68342 13.0976 9.31658 13.0976 9.70711 12.7071L13.7071 8.70711Z" fill="#F7FEE7"/>
</svg>

onClose={continueStartWorkspace}
closeable={false}
onEnter={continueStartWorkspace}
title="User Validation Successful"
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: Given it's not ideal to update the modal title based on the user action and taking into account the relevant discussion (internal), what do you think of moving the alert component on the loading screen directly where we later on show the warnings about the latest editor release and skip asking users to acknowledge the account validation?

Latest Release on Workspace Start Validation on Workspace Start
Screenshot 2022-08-24 at 2 52 07 PM (2) Screenshot 2022-08-24 at 2 51 18 PM (2)

Copy link
Member Author

Choose a reason for hiding this comment

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

I think an explicit, "you are verified" message with a 'continue' button is clearer. Especially because we are reloading the page.

Copy link
Contributor

@gtsiolis gtsiolis Aug 24, 2022

Choose a reason for hiding this comment

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

thought: It is certainly clearer and gives users the time to acknowledge this. 💯

question: What do you think of keeping modal, and updating the modal title to be the same on all cases, and rely on modal body (paragraph and alerts) to inform the user about validation errors or successful validation?

@svenefftinge svenefftinge force-pushed the sefftinge/add-phone-verification-11339 branch 3 times, most recently from b6d31bc to 8fa474f Compare August 24, 2022 14:25
@svenefftinge svenefftinge marked this pull request as ready for review August 24, 2022 14:38
@svenefftinge
Copy link
Member Author

I'll hide this behind a feature flag so we can carefully enable it in prod.

@svenefftinge svenefftinge force-pushed the sefftinge/add-phone-verification-11339 branch from 8fa474f to 979777c Compare August 24, 2022 17:00
@svenefftinge
Copy link
Member Author

svenefftinge commented Aug 24, 2022

/werft run

👍 started the job as gitpod-build-sefftinge-add-phone-verification-11339.33
(with .werft/ from main)

@AlexTugarev AlexTugarev self-assigned this Aug 25, 2022
Copy link
Contributor

@corneliusludmann corneliusludmann left a comment

Choose a reason for hiding this comment

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

It seems that this PR does not have changes in Team Self-Hosted owned files anymore. However, from self-hosted perspective, it looks good so far.

‼️ Hint: You should rebase shortly before you merge this and let golden files of the installer render tests be re-generated since there are new tests in main already that will let fail the main build after merging. Sorry for the inconvenience.

Copy link
Member

@AlexTugarev AlexTugarev left a comment

Choose a reason for hiding this comment

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

The account verification works nicely 🎉

I left some comment, and I'm happy to discuss the additionalData comment 👍🏻
Ah, the only real issue so far is that old GH account are not count as verified, because the check is not performed after signup, but rather after the following re-login.

@@ -51,6 +51,8 @@ function getConfig(config: RateLimiterConfig): RateLimiterConfig {
getLoggedInUser: { group: "default", points: 1 },
getTerms: { group: "default", points: 1 },
updateLoggedInUser: { group: "default", points: 1 },
sendPhoneNumberVerificationToken: { group: "default", points: 1 },
Copy link
Member

Choose a reason for hiding this comment

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

does this mean it would be allowed to call it full 1k per interval?
if the external service imposes a rate limit on our registration, we'd be exposed to random DoS attacks, no?
in that case let's introduce a config with a reasonable limitation of several tries per 10s – similar to create workspace and the like.

Copy link
Member

Choose a reason for hiding this comment

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

I guess this also applies to verifyPhoneNumberVerificationToken.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, yes. Probably worth limiting this per user on our side, although there is a rate limit per phone number on twilio as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm introducing a new category phoneVerification with 10 points per 10 seconds.

@@ -66,6 +66,9 @@ export class UserDeletionService {
this.deleteIdentities(user);
await this.deleteTokens(db, user);
user.markedDeleted = true;
if (user.additionalData) {
delete user.additionalData.lastVerificationTime;
Copy link
Member

Choose a reason for hiding this comment

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

I guess we'd need to delete the phone number as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

I intentionally left it in order to follow up in case the user did something bad.

Copy link
Member

Choose a reason for hiding this comment

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

That we definitely need to check with data privacy. To prevent recreating accounts it would be sufficient to store a hash of the phone number, no?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, I'll store the hash instead and introduce d_b_blocked_phone_hashesthat we update on user block/unblock. Also will check that table on first sign up.

Copy link
Member Author

Choose a reason for hiding this comment

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

Update: I'm leaving the blocked_phone_hashes out of this PR, as it makes it even bigger (it already is big).
But I'm moving the two properties into d_b_user.

@@ -419,6 +422,11 @@ export class UserService {
user.name = user.name || authUser.name || authUser.primaryEmail;
user.avatarUrl = user.avatarUrl || authUser.avatarUrl;

if (authUser.created_at && isDateSmaller(authUser.created_at, daysBefore(new Date().toISOString(), 30))) {
Copy link
Member

Choose a reason for hiding this comment

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

updateUserOnLogin is not called on signups. that means, a new Gitpod account for an old GH account is not considered trusted.
for a complete picture, I think this should not be updated on logins (but rather on signups,) otherwise they might just create a pile of GH account and start using them after a while.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch! 🙏

Copy link
Member

@AlexTugarev AlexTugarev Aug 26, 2022

Choose a reason for hiding this comment

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

Let's move this check to createUser, that should fix Test 2.

Copy link
Member Author

Choose a reason for hiding this comment

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

moved it to the callback that is passed to createUser because I needed access to the authUser object. Also looked like a good pace for it otherwise. Fine?

for (const p of allowedFields) {
if (p in partialUser) {
(user[p] as any) = partialUser[p];
}
}
// special treatment of nested additionalData
if (partialUser.additionalData) {
Copy link
Member

Choose a reason for hiding this comment

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

Preferably, let's not use a field dedicated for user preferences/settings for something that actually controls the wheels or breaks. That check seems to work fine, I've tried to break without success, but I'd prefer not to have a this guarded places in a field which otherwise is free to change for users.

There is an option that we can use to add field to tables without the downside of locking tables, we just need to add NOT NULL DEFAULT '', ALGORITHM=INPLACE, LOCK=NONE for the alter statement.

As we used this alternative the last time(s) discussed this matter, I'd prefer to stick to one pattern 🙏🏻

Copy link
Member Author

Choose a reason for hiding this comment

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

Which discussion do you refer to?

Copy link
Member

Choose a reason for hiding this comment

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

To avoid implementing the checks for nested attributes in the additionalData bucket we've moved the attribution override to User in #11522. The validation necessary is to disallow users setting the attribution target to a team id they are not a member of.


async mayStartWorkspace(
user: User,
date: Date = new Date(),
runningInstances: Promise<WorkspaceInstance[]>,
): Promise<MayStartWorkspaceResult> {
try {
const verification = await this.verificationService.needsVerification(user);
Copy link
Member

Choose a reason for hiding this comment

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

Nice integration! Fits in perfectly.

That reminds me of the ToS flow integration, which intercepted the signup, but it would actually be better to use this kind of hook.

if (!this.verifyService) {
throw new Error("No verification service configured.");
}
const verification_check = await this.verifyService.verificationChecks.create({
Copy link
Member

Choose a reason for hiding this comment

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

try-catch, otherwise it's handled as internal server error whenever one uses the happy finger on the send button.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't get this comment. Why is there always an internal server error?

Copy link
Member

Choose a reason for hiding this comment

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

as this is not wrapped in a ResponseError it'll be logged automatically as an internal server error, thus it's loud on the logs, and it seems not to be rendered properly on frontend, at least I couldn't see twillios rate limiting error rendered on frontend.

Copy link
Member Author

Choose a reason for hiding this comment

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

It is fine to be loud on the logs. It's not a user error when an error is thrown but something with the system. The error is thrown to frontend and I'll make it so that the user gets feedback about the error.

@kylos101
Copy link
Contributor

👋 @svenefftinge, would it be possible to include bitbucket with this too, or perhaps in a follow-on PR? I ask because of this thread.

@svenefftinge
Copy link
Member Author

The logic is opt-out. So all bitbucket users need to verify.

@svenefftinge svenefftinge force-pushed the sefftinge/add-phone-verification-11339 branch 2 times, most recently from 8e00464 to 3dc9ac6 Compare August 26, 2022 14:24
@svenefftinge
Copy link
Member Author

svenefftinge commented Aug 26, 2022

/werft run

👍 started the job as gitpod-build-sefftinge-add-phone-verification-11339.37
(with .werft/ from main)

@jldec
Copy link
Contributor

jldec commented Aug 27, 2022

/werft run with-clean-slate-deployment=true

👍 started the job as gitpod-build-sefftinge-add-phone-verification-11339.38
(with .werft/ from main)

@jldec
Copy link
Contributor

jldec commented Aug 27, 2022

/werft run with-clean-slate-deployment=true

👍 started the job as gitpod-build-sefftinge-add-phone-verification-11339.40
(with .werft/ from main)

@@ -148,6 +149,9 @@ export class UserDeletionService {
user.avatarUrl = "deleted-avatarUrl";
user.fullName = "deleted-fullName";
user.name = "deleted-Name";
if (user.verificationPhoneNumber) {
user.verificationPhoneNumber = "deleted-verificationPhoneNumber";
Copy link
Member

Choose a reason for hiding this comment

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

ER_DATA_TOO_LONG: Data too long for column 'verificationPhoneNumber'

we're almost there, just 2 chars too long.

Copy link
Member Author

Choose a reason for hiding this comment

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

what did you type in?

Copy link
Member Author

Choose a reason for hiding this comment

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

ah, got it.

Copy link
Member

Choose a reason for hiding this comment

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

That happens on deletion of an account, which actually breaks the process.

Copy link
Member

Choose a reason for hiding this comment

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

deleted-phoneNumber with a length of 20 should work just fine.

@svenefftinge svenefftinge force-pushed the sefftinge/add-phone-verification-11339 branch from 3dc9ac6 to 1044b42 Compare August 29, 2022 07:17
Copy link
Member

@AlexTugarev AlexTugarev left a comment

Choose a reason for hiding this comment

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

LGTM!
Tested works as advertised.

Logic for Test 1 ++ isn't there is code, which I think is not needed at all ;-)

@roboquat roboquat merged commit 79b75ab into main Aug 29, 2022
@roboquat roboquat deleted the sefftinge/add-phone-verification-11339 branch August 29, 2022 07:36
@roboquat roboquat added the deployed: webapp Meta team change is running in production label Aug 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
deployed: webapp Meta team change is running in production release-note size/XXL team: delivery Issue belongs to the self-hosted team team: webapp Issue belongs to the WebApp team
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Add Phone Verification for new Users
7 participants