Skip to content
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

Login via jwt #7226

Open
wants to merge 6 commits into
base: alpha
Choose a base branch
from
Open

Conversation

jdposthuma
Copy link
Contributor

@jdposthuma jdposthuma commented Feb 24, 2021

New Pull Request Checklist

Issue Description

This PR deepens Parse's support of JWT by checking for the the req.userFromJWT on login. This is helpful so that clients can be minimally effected while implementing new authorization sources. For example, a migration to AWS Cognito might look like this:

Cognito.signIn(username, password).then(response => {
	const { email } = response.signInUserSession.idToken.payload;
	Parse.serverAuthType = 'Bearer';
	Parse.serverAuthToken = `${idToken.getJwtToken()}`;
	return Parse.User.logIn(email, '');
}).then(parseUser => {...})

Related issue: #6390

Approach

This PR seeks to be minimally invasive by building on @sunshineo's original work.

TODOs before merging

@ghost
Copy link

ghost commented Feb 24, 2021

Danger run resulted in 1 warning; to find out more, see the checks page.

Generated by 🚫 dangerJS

@codecov
Copy link

codecov bot commented Feb 24, 2021

Codecov Report

Merging #7226 (2eecdb5) into master (7f47b04) will increase coverage by 0.01%.
The diff coverage is 94.28%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #7226      +/-   ##
==========================================
+ Coverage   94.00%   94.01%   +0.01%     
==========================================
  Files         172      172              
  Lines       12835    12925      +90     
==========================================
+ Hits        12066    12152      +86     
- Misses        769      773       +4     
Impacted Files Coverage Δ
src/cloud-code/Parse.Cloud.js 98.73% <ø> (ø)
src/Adapters/Storage/Mongo/MongoStorageAdapter.js 92.92% <57.14%> (-0.60%) ⬇️
src/Routers/UsersRouter.js 94.18% <92.85%> (-0.19%) ⬇️
src/LiveQuery/ParseLiveQueryServer.js 95.33% <94.93%> (+0.40%) ⬆️
src/Adapters/Auth/facebook.js 92.06% <95.23%> (+5.10%) ⬆️
src/triggers.js 94.92% <95.45%> (+0.32%) ⬆️
src/batch.js 91.37% <95.65%> (-0.93%) ⬇️
src/ParseServerRESTController.js 97.01% <96.00%> (-1.35%) ⬇️
src/Adapters/Auth/instagram.js 91.66% <100.00%> (-8.34%) ⬇️
src/Controllers/LiveQueryController.js 96.42% <100.00%> (-0.13%) ⬇️
... and 7 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7f47b04...b2b525d. Read the comment docs.

@davimacedo
Copy link
Member

Could you please add some test cases so we can better understand the feature?

@dplewis
Copy link
Member

dplewis commented Feb 25, 2021

@sunshineo If I remember correctly you were using JWT right?

@sunshineo
Copy link
Contributor

@dplewis Yes. I submitted #6411 and you merged it. I'm not sure what is new in this one

@jdposthuma
Copy link
Contributor Author

Could you please add some test cases so we can better understand the feature?

@davimacedo - done. Thanks for keeping me honest :)

@jdposthuma
Copy link
Contributor Author

I also updated description (complete with a code snippet!) to better explain my implementation

@jdposthuma
Copy link
Contributor Author

@davimacedo - got a minute to look at this?

@davimacedo
Copy link
Member

Sorry for the delay but I am not sure if I understood the purpose of the PR yet. Why would someone call the login endpoint in this scenario? Would we expect what data to be sent? And what data would we return? I'd be more comfortable seeing a test case of the api level in which you could have some middleware doing the jwt validation and the whole process in place so we can have a better vision of the big picture. I think it is also important to add some documentation for the developers learn how to use it.

@jdposthuma
Copy link
Contributor Author

Sorry for the delay but I am not sure if I understood the purpose of the PR yet. Why would someone call the login endpoint in this scenario? Would we expect what data to be sent? And what data would we return? I'd be more comfortable seeing a test case of the api level in which you could have some middleware doing the jwt validation and the whole process in place so we can have a better vision of the big picture. I think it is also important to add some documentation for the developers learn how to use it.

It's important so that the Parse SDKs can transparently log users in (and get all of the stateful changes caused by a login) while making minimal in the apps. Make sense?

The middleware would be exactly the same as for the original feature that injects req.userFromJWT.

Happy to document this, but where? The only documentation I found on this was on the original github issue.

@davimacedo
Copy link
Member

I think we can document the feature at Parse Server guide: https://docs.parseplatform.org/parse-server/guide/

@jdposthuma
Copy link
Contributor Author

I think we can document the feature at Parse Server guide: https://docs.parseplatform.org/parse-server/guide/

Done. parse-community/docs#819

@davimacedo
Copy link
Member

Thanks for the PR on the docs. It will be very helpful for other developers. I am not 100% convinced we should add this login workaround though. It looks really weird to me we recommend developers using JWT to call the login endpoint which does not perform any kind of validation. Parse Server will generate a new session token that can expire, will be associated to a user, etc, and maybe there are other implications that we cannot see here now. Also, even if we merge it, I think we should consider it a breaking change, since password will be just ignored in the case that a jwt token is sent. What are the stateful changes that you are interested in the client side? Maybe we should find a way to solve that in the SDK side and not in the server. Even a solution using Parse.User.become() that would call the Parse Server /me endpoint would look more appropriate to me. @mtrezza @dplewis @sunshineo thoughts?

@sunshineo
Copy link
Contributor

I can only provide some context on how we were able to support JWT, why I submitted a PR, and after the PR was merged how it become easier to use JWT.

We are using Parse as a library in a node express project and handling /parse url. Our frontend authorize user using Auth0 and get a JWT. And that will be the only thing our frontend will save and use. So our server need to support it, we wrote express code before mount the Parse library but also on the /parse url. The code will decode and validate the JWT and get the username. Then we try to find the user based on username, if no such user we create the user. Then we try to find the session based on the user, if no such session we create the session (with a 10 year expiration which is the max allowed). Then we put the session token on the request header, and call next(). Parse will then take over as if the request came with a session token on the header of the request. Then it will find the user based on the session token.

So you see there is a few wasted calls, and that will be on every request. Maybe we can cache everywhere to make it faster but I decided to submit a PR which allows me to instead of add the session token, add the user directly on the request and use that. So no more user -> session -> user back and forth. No more hacks like create session with a 10 year expiration, etc.

@jdposthuma
Copy link
Contributor Author

jdposthuma commented Mar 4, 2021

I think what @sunshineo and I are trying to do is a growing use case. Essentially - using a completely external authentication scheme while continuing to leverage the authorization mechanism of the parse user. In my case, I'm using Cognito.

@davimacedo - I hear your concerns that this is a bit of a workaround. My org uses a lot of Parse Server functionality, including push notifications and live queries. If there's no login event, I think the installation entity isn't properly initialized, among other things. Right?

Perhaps the PR doesn't go far enough. What if we make the following 2 changes:

  1. Modify the Parse.User.login() to accept no args so it's clear to developers that credentials must be provided elsewhere.
  2. Modify the token generation logic to use the JWT in place of generating a random token. This still maintains the sufficient cryptographic entropy due to the JWT signature while ensuring continuity throughout the application (eg: /me will still function). The only downside is some additional overhead of the longer token length of JWT.

@davimacedo
Copy link
Member

We'd need to test, but I'd try to add a logic similar to what you did in this PR at the /me endpoint: https://github.com/parse-community/parse-server/blob/master/src/Routers/UsersRouter.js#L142

Once that's done, I believe that in client side you'd need only to call Parse.User.become() and you'd have the current user state in place. From the server side point of view, it looks more reasonable to me.

@jdposthuma
Copy link
Contributor Author

But this won't manage the user state in the ways that I need (eg: setting the installationId). The functionality here still needs to execute when I'm logging in with my JWT: https://github.com/parse-community/parse-server/blob/master/src/Routers/UsersRouter.js#L213-L241

@davimacedo
Copy link
Member

Do you mean that you need the beforeLogin trigger to be executed or you need a new session token to be created with associated installation id?

@jdposthuma
Copy link
Contributor Author

For the immediate use-case, I need the session to be created. However, to create a simplified development experience, I'd also like to support the triggers.

@jdposthuma
Copy link
Contributor Author

@mtrezza @davimacedo - any word on this?

@davimacedo
Copy link
Member

I'm still not sure the best way to have it in Parse Server (I still believe your use-case should be handled in the SDK and not in the server). But I have an idea on how you can solve your needs. Why don't you write a middleware before parse-server is mounted to handle the login endpoint?

@jdposthuma
Copy link
Contributor Author

Why don't you write a middleware before parse-server is mounted to handle the login endpoint?
It's brittle and not DRY, as it requires me to re-create the login sequence with private function calls.

One of the reasons I and my team like this solution is precisely because it doesn't require significant changes the SKDs or app logic to implement. I'm all ears for a better suggestion, but implementing the same basic logic in each of the SKDs doesn't make sense to me when we could simply allow a login with alternate credentials. IMO, the benefits are clear:

  • improved security
  • makes the Parse ecosystem more accommodating to common use case

I appreciate you trying to throw me a bone on this one (and keeping up the banter for 2 weeks!).

@davimacedo
Copy link
Member

I appreciate you trying to throw me a bone on this one (and keeping up the banter for 2 weeks!).

I am not sure what you mean with that but I'm doing my best to help you with some alternatives to achieve what you need in your use-case.

Again, I'm particularly not comfortable in adding this workaround to the login endpoint, since it is not actually performing a log in validation, and I believe that we are not in the right track to create the jwt feature for the whole ecosystem. Maybe we should first discuss how jwt could be added to the project in a more sustainable way, considering login, logout, session tokens, etc.

But that's my particular opinion and maybe I am not smart enough to understand the pros of this PR. Anyways the PR can also be approved and merged by any of the other project maintainers willed to support the idea. @dplewis @mtrezza @TomWFox thoughts?

@mtrezza
Copy link
Member

mtrezza commented Mar 13, 2021

One of Parse Server's strength is its modularity, think adapters and controllers. Have you thought about integrating it as an optional module / adapter?

We also have a few open PRs regarding auth / login. I'm not in detail familiar with the scope and state of these, but maybe there is a relation that should be considered.

@mtrezza mtrezza mentioned this pull request Mar 13, 2021
7 tasks
@davimacedo
Copy link
Member

That would make more sense to me. We could have something like a tokenAdapter which could be setup via parse server config and could have some methods like, handleLogin, handleLogout, createToken, etc...

@mtrezza
Copy link
Member

mtrezza commented Sep 3, 2021

⚠️ Important change for merging PRs from Parse Server 5.0 onwards!

We are planning to release the first beta version of Parse Server 5.0 in October 2021.

If a PR contains a breaking change and is not merged before the beta release of Parse Server 5.0, it cannot be merged until the end of 2022. Instead it has to follow the Deprecation Policy and phase-in breaking changes to be merged during the course of 2022.

One of the most voiced community feedbacks was the demand for predictability in breaking changes to make it easy to upgrade Parse Server. We have made a first step towards this by introducing the Deprecation Policy in February 2021 that assists to phase-in breaking changes, giving developers time to adapt. We will follow-up with the introduction of Release Automation and a branch model that will allow breaking changes only with a new major release, scheduled for the beginning of each calendar year.

We understand that some PRs are a long time in the making and we very much appreciate your contribution. We want to make it easy for PRs that contain a breaking change and were created before the introduction of the Deprecation Policy. These PRs can be merged with a breaking change without being phased-in before the beta release of Parse Server 5.0. We are making this exception because we appreciate that this is a time of transition that requires additional effort from contributors to adapt. We encourage everyone to prepare their PRs until the end of September and account for review time and possible adaptions.

If a PR contains a breaking change and should be merged before the beta release, please mention @parse-community/server-maintenance and we will coordinate with you to merge the PR.

Thanks for your contribution and support during this transition to Parse Server release automation!

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.

None yet

5 participants