8000 Separate OAuth2 data out of Sessions and into User Identities by stnguyen90 · Pull Request #5551 · appwrite/appwrite · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Separate OAuth2 data out of Sessions and into User Identities #5551

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

Closed
wants to merge 7 commits into from

Conversation

stnguyen90
Copy link
Contributor
@stnguyen90 stnguyen90 commented May 18, 2023

What does this PR do?

(Provide a description of what this PR does and why it's needed.)

Test Plan

Manual

New User - New Session - No Matching Identities

image

New User - New Session - Matching provider identity

No new user or identity created. Only new session.

Before:

image

After:

image

New Session - Matching Another User's Email Identity

If there is an identity with the same email already, there should be an error:

Before:

image

Error:

image

Existing User - No Identities - New Session

This should create a new session successfully for backwards compatibility on mobile platforms where we still need to make sure an existing session is used.

Before:

image

After:

image

Existing (matching) User - No Identies - Existing Session

This should create a new identity:

Before:

image

After:

image

Existing (non-matching) User - No Identities - New Session

Trying to connect an identity where providerEmail matches another user should result in error.

Before:

image

Error:

image

Deleting a User Deletes the Identities

Delete job deleted 64b1c0422411984aa897:

image

Which was the identity:

image

Related PRs and Issues

Checklist

  • Have you read the Contributing Guidelines on issues?
  • If the PR includes a change to an API's metadata (desc, label, params, etc.), does it also include updated API specs and example docs?

@stnguyen90 stnguyen90 marked this pull request as draft May 18, 2023 01:12
@stnguyen90 stnguyen90 changed the base branch from master to 1.4.x May 18, 2023 01:12
@stnguyen90 stnguyen90 force-pushed the feat-connected-accounts branch from ecde406 to 1383ce9 Compare June 23, 2023 21:17
@stnguyen90 stnguyen90 force-pushed the feat-connected-accounts branch 2 times, most recently from 76ab919 to 345c70c Compare July 15, 2023 00:29
@@ -47,6 +47,14 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
$email = \strtolower($email);
}

// Makes sure this email is not already used in another identity
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
Query::equal('providerEmail', [$email]),
Copy link
Member

Choose a reason for hiding this comment

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

Are we making sure to attach only verified emails? Otherwise anyone could fake an email in a 3rd party provider and gain access to it on Appwrite.

Copy link
Contributor Author
@stnguyen90 stnguyen90 Jul 17, 2023

Choose a reason for hiding this comment

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

We're actually not really checking verified at all today because not all providers verify. Notice how $isVerified isn't used anywhere:

$isVerified = $oauth2->isEmailVerified($accessToken);

\curl_close($ch);

if ($code != 200) {
$dump = function($value) {
Copy link
Member

Choose a reason for hiding this comment

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

Reminder, remove debugs.

@@ -98,6 +99,14 @@
}
}

// Makes sure this email is not already used in another identity
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
Copy link
Member

Choose a reason for hiding this comment

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

Should a user be able to log in with an email associated solely with the provider that is different than the main email?

Are we blocking users to attach OAuth providers with emails associated to other users?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should a user be able to log in with an email associated solely with the provider that is different than the main email?

Using OAuth2, If the email is tied to an existing user via an identity, they won't be able to log in. That's what this check does:

// Makes sure this email is not already used in another identity
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
Query::equal('providerEmail', [$email]),
]);
if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) {
$failureRedirect(Exception::USER_EMAIL_ALREADY_EXISTS);
}

Using OAuth2, If the email is not associated with an existing identity or user, a new user will be created.

Using Create Email Session, a user must use their main user email and cannot use an identity email.

Are we blocking users to attach OAuth providers with emails associated to other users?

Yes, here:

// Check if this identity is connected to a different user
if (!$user->isEmpty()) {
$userId = $user->getId();
$identitiesWithMatchingEmail = $dbForProject->find('identities', [
Query::equal('providerEmail', [$email]),
Query::notEqual('userId', $userId),
]);
if (!empty($identitiesWithMatchingEmail)) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
}


// Delete identities
$this->deleteByGroup('identities', [
Query::equal('userId', [$userId])
Copy link
Member

Choose a reason for hiding this comment

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

Use internalId for the deletion of child documents.


public function getError() : string
{
return $this->error;
Copy link
Member

Choose a reason for hiding this comment

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

Why aren't we using the native getMessage interface?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the error from the oauth2 provider. The message will include error and error_description.

See https://datatracker.ietf.org/doc/html/rfc6749#section-5.2

@stnguyen90 stnguyen90 force-pushed the feat-connected-accounts branch from 345c70c to 66376de Compare July 17, 2023 21:44
@stnguyen90 stnguyen90 requested a review from fogelito July 17, 2023 21:44
@stnguyen90 stnguyen90 force-pushed the feat-connected-accounts branch 5 times, most recently from 434da25 to a906f23 Compare July 17, 2023 22:21
@stnguyen90 stnguyen90 requested a review from eldadfux July 17, 2023 22:22
'$id' => ID::custom('_key_provider_providerUid'),
'type' => Database::INDEX_UNIQUE,
'attributes' => ['provider', 'providerUid'],
'lengths' => [100, 100],
Copy link
Contributor

Choose a reason for hiding this comment

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

Provider size is 128 , and providerUid is 2048 (does it have to be so long?)
So will do :
'lengths' => [128, 640],
Since both together is the max of 768

Copy link
Contributor Author

Choose a reason for hiding this comment

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

and providerUid is 2048 (does it have to be so long?)

providerUid in the sessions collection is 2048, so I wanted to make it consistent. Is that okay?

$userId = $user->getId();

$identitiesWithMatchingEmail = $dbForProject->find('identities', [
Query::equal('providerEmail', [$email]),
Copy link
Contributor

Choose a reason for hiding this comment

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

I do not see an index for providerEmail

$failureRedirect(Exception::USER_BLOCKED); // User is in status blocked
}

$identity = $dbForProject->findOne('identities', [
Copy link
Contributor

Choose a reason for hiding this comment

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

To the existing userInternalId index , you can add these 2 attributes: provider, providerUid
['userInternalId', 'provider', 'providerUid']

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're suggesting to make sure there's an index on this query (userInternalId, provider, and providerUid)?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes , since there is a unique index on [provider,providerUid],
I think adding an additional index on the above will be good


$filterQueries = Query::groupByType($queries)['filters'];

$results = $dbForProject->find('identities', $queries);
Copy link
Contributor

Choose a reason for hiding this comment

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

Since $queries are dynamic, will have to add indexes for the most common attributes, that will be queried for

@stnguyen90 stnguyen90 force-pushed the feat-connected-accounts branch from a906f23 to 5a52151 Compare July 20, 2023 05:04
@stnguyen90 stnguyen90 requested a review from fogelito July 20, 2023 05:04
@stnguyen90 stnguyen90 force-pushed the feat-connected-accounts branch from 5a52151 to 4cdbd65 Compare July 20, 2023 22:02
@stnguyen90 stnguyen90 marked this pull request as ready for review July 21, 2023 21:58
@stnguyen90 stnguyen90 changed the title Feat connected accounts Separate OAuth2 data out of Sessions and into User Identities Jul 22, 2023
This allows us to retain the OAuth2 info even if the session is
deleted. This also provides a foundation for allowing multiple emails,
phone numbers, etc, not from an OAuth2 provider.
Setting a password can cause problems with other APIs that expect the
password to be null. In addition, it doesn't match the implementation
for the other APIs that create a user without a password (Create Magic
URL Session, Create Phone Session, Create Anonymous Session, etc).
Update the OAuth2 class to throw an exception if an API call to the
OAuth2 provider fails and update the endpoint to redirect to the
failure url with the information from the OAuth2 provider.
Prevent missing project ID error.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0