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

Access cookies or req in jwt callback (and/or other callbacks) #543

Closed
bscaspar opened this issue Aug 4, 2020 · 5 comments
Closed

Access cookies or req in jwt callback (and/or other callbacks) #543

bscaspar opened this issue Aug 4, 2020 · 5 comments
Labels
enhancement New feature or request

Comments

@bscaspar
Copy link
Contributor

bscaspar commented Aug 4, 2020

Summary
Provide req object or cookies as an arg in the jwt callback (and maybe others if folks are interested).

Purpose of proposed feature
We have a unique login flow - a user fills out their Instagram handle in the login page and then uses Facebook auth. On login, we need to validate that the user's authenticated Facebook account has access to the Instagram handle they submitted and confirm that handle is on our greenlist. After the initial Facebook login, we use the the JWT callback to get the list of Instagram accounts that Facebook account has access to from the FB graph API. At this point, we need to compare this list of Instagram account handles with the original handle submitted in the login form.

I would like to do this in the JWT callback so I can store the Instagram account ID in the JWT and access it through getSession. The only way I can think of to get access to that handle, submitted to a form during login, is to store it in a cookie and access it here. I believe using a database might help solve this problem, but that's not a possibility for me right now, so the goal is to do this without.

(Is there another way to pass data to the JWT callback from a form?)

Details
Most basic/obvious implementation would be to update this:

const jwtPayload = await callbacks.jwt(defaultJwtPayload, user, account, OAuthProfile, isNewUser)

with something like

const jwtPayload = await callbacks.jwt(defaultJwtPayload, user, account, OAuthProfile, isNewUser, req)

Potential problems
I am far from an auth expert and can only assume I'm missing an important security reason this isn't recommended.

Workaround
Currently I have this built into a post-login redirect route that fetches data from an API route to determine if the handle matches the Instagram accounts. It signs the user out if there isn't a match, and stores the data in a cookie if there is.

@bscaspar
Copy link
Contributor Author

bscaspar commented Aug 6, 2020

Also related: #469

@bscaspar
Copy link
Contributor Author

bscaspar commented Aug 6, 2020

I'm realizing this brute force approach may give a little too much leeway to the developer. Giving folks (especially those with limited auth experience like myself) the ability to access this req object in a callback may enable some unsafe behavior along the lines of assigning unsecured password-like data from a login screen to a cookie.

Aggregating other implementations that have been suggested:

@iaincollins
Copy link
Member

iaincollins commented Aug 7, 2020

@bscaspar Thanks for the follow up!

FWIW I agree, I'm worried about exposing req or res objects as it's better to abstract that away if possible (it's really easy to create new and confusing problems by using them and tends to result in people getting stuck and asking for help more).

I think additional parameters and/or additional callbacks is general preferable approach.

In this case, if you are integrating Facebook and Instagram I'd suggest ideally persisting users to a database and creating an API endpoint like /api/user/instagram which does some fo the work (e.g. calls getSession() or uses the getToken() helper to get the User, then looks up the associated Instagram account info and returns a response based on what it finds.

The front end could then prompt the user to take action if needed, if the data is missing or invalid.

A database is likely easier - it's technically possible to do a lot with using JWT without one, but it can be more work - but moving some of the logic for this to a dedicated API endpoint that uses getSession() or getToken() and using fetch() or something like useSWR() to check it, should make it much easier if you aren't using a database.

Moving some logic out of the jwt callback is always good - is sometimes not avoidable, but is great if you can as improves performance and avoids potential rate limiting issues by avoiding calling out to a third party service when the client performs a session check.

@bscaspar
Copy link
Contributor Author

bscaspar commented Aug 8, 2020

Thanks for the follow up! I guess I should look into using a DB after successfully avoiding it due to sheer laziness 😄

It looks like there may be some momentum towards the parameters getting passed to signIn once #541 is successfully merged so I'll keep an eye on that.

@bscaspar bscaspar closed this as completed Aug 8, 2020
@dan-kwiat
Copy link
Contributor

Isn't it already possible to access req in callbacks? e.g.

// [...nextauth].js
export default (req, res) => NextAuth(req, res, getOptions(req))

Is there any downside to this approach?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants