vercel / next.js

The React Framework
https://nextjs.org
MIT License
121.33k stars 25.96k forks source link

Add login / authentication example #153

Closed rauchg closed 5 years ago

rauchg commented 7 years ago

With:

I think this will be hugely helpful to a lot of newcomers.

sedubois commented 7 years ago

@impronunciable FYI with your example I don't yet know how to handle sessions, as I want to get rid of passwordless. I'll try adapting @iaincollins' example in my app.

My requirements are:

sedubois commented 7 years ago

In case it helps someone here's my app with server-side auth πŸ™‚

The auth should be split out from Next.js server but I'm waiting for someone else to give inspiration about that... Also I'm not sure if it's properly protected against CSRF.

davibe commented 7 years ago

This is what i did:

This is vulnerable to XSS (even if react does a lot to prevent it) and CSRF attacks but it is simple and works with SSR.

balupton commented 7 years ago

Just to add to the requests

graph.cool + apollo + jwt + auth0 + next.js, the first 4 parts of that are already done at https://github.com/graphcool-examples/react-apollo-auth0-example

nickredmark commented 7 years ago

@balupton in your example what happens when the token expires while the user is still connected, is the session just over or is it renewed somehow?

balupton commented 7 years ago

@nmaro don't know, not written by me - best to ask over there

For my own app, I've got auth going with next.js and auth0, then with a zeit/micro API server that verifies the bearer tokens.

Should be able to open-source sometime in February.

nickredmark commented 7 years ago

Following the same philosophy that next.js has been following (do one thing and do it well), I've developed the skeleton of an extensible user accounts system for node. See the philosophy here: https://medium.com/the-ideal-system/ooth-user-accounts-for-node-js-93cfcd28ed1a#.97kyfg4xg

The github project is here: https://github.com/nmaro/ooth/

The goal is to have an extensible, independent authentication + user management service that's ideal to be used as a separate microservice, an application structure that would play very well with node.js.

An example with email+password authentication is here: https://github.com/nmaro/ooth/tree/master/examples/ooth There are already packages in place for Facebook and Google auth (ooth-facebook, and ooth-google), which should be easy to implement based on passport-facebook and passport-google respectively.

I'll be posting a next.js integration example asap. Feel free to join the discussion and contribute.

Sorry for the plug, but it's for the greater good - I really believe there is no good solution for node yet, and this is just the right audience of people who might want such a thing. Peace

nickredmark commented 7 years ago

Meanwhile... here is an example graphql API that requires authentication with a JWT-Token just for write operations. Feel free to use it with your favorite authentication method :)

https://github.com/nmaro/ooth/tree/master/examples/graphql-api-with-auth

iaincollins commented 7 years ago

I've updated https://nextjs-starter.now.sh to add oAuth support.

screen shot 2017-02-10 at 05 03 19

The nature of oAuth - db+sessions+passport and error handling needing to work closely together - and of sessions needing to work both client and server and how that works with universal rendering - means a little bit much to try and wrap around at once if you are trying to figure out what is going on, but the client logic doesn't have any oAuth specific configuration so it's not too messy.

I'd be happy to split off just authentication into a separate example, although I suspect it wouldn't be a lot smaller. If someone else wants to, great. I'll probably add a little more to the example at some point (like an account management page). Probably linking more to the documentation would be good.

timneutkens commented 7 years ago

Amazing work! If you could split the auth And add it to the next.js repo that would be awesome :heart:

timneutkens commented 7 years ago

I can do it too btw πŸ‘πŸ»

iaincollins commented 7 years ago

I won't have time over the next 2 weeks or so, so if someone wants do that would be great.

I'd love to refactor it and see if it could be reduced to just a module (that exposes simple components like login buttons and sign in forms to embed), and to take some inspiration from the really nice earlier client-side-only example by @impronunciable.

Update: I'm actually going away, which is why I can't, but when I'm back I'm happy to look at doing that!

zhaoyao91 commented 7 years ago

I've followed the auth0/react quick start guide with some modification, but when I call lock.show(), the app complaints:

Uncaught Error: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded

davibe commented 7 years ago

@iaincollins @timneutkens regarding your example, please correct me if I am wrong.

The example uses an httpOnly cookie to store a session token making it safe against javascript injiection attacks (XSS). Then it goes into the trouble of having a csrf token stored in local storage to also protect against CSRF attacks.

There is an underlying assumption that this combination of techniques makes things safe, which could mislead the user/developer. An attacker can still injiect javascript in the page (XSS), read the csrf token, and use it to perform authenticated (cookie) requests to the apis. Is worth mentioning in the readme ?

iaincollins commented 7 years ago

Hi @davibe

Alas, there isn't currently a technically better approach than a session cookie and a separate rotating CSRF token, so guess I have to say I'm broadly happy with the model as is it as there isn't really another way to do it (even if the actual implementation could always be improved).

We could add browser finger printing, maybe the session token could rotate more frequently (I forget if that is automatic right now), the CSRF token could be a header instead of param, the cookies should really be SSL only in production and we could add an expiry date to the sign in tokens, but AFAICS there's fairly limited scope for improvement as far as the security model goes and and nothing that really grant further protection in the event someone is able to inject any code they like into to an app; but please do feel free to raise those things as issues for improvements in the repo.

If there were any account status modifying options (which there aren't currently) we could have a CAPTCHA before performing them to make it more difficult to make changes on the server without permission, but all that happens in the example is users can sign in and out so that's currently out of scope.

Storing session tokens local storage would make it notably less secure and go against the intended use in the spec, so personally I'm not in favour of that - though I appreciate it would simplify things, as does not having CSRF projection, but people probably don't need an example of that as it would be very simple to do - I've only tried to provide one because it is so awkward. :-)

I am fully with you in that I too hate how necessarily awkward it is and have not given up on trying to turn it into a module.

davibe commented 7 years ago

Hmm.. I understand. This issue was very useful to me, thank you.

This is the point of view of Auth0 https://auth0.com/blog/cookies-vs-tokens-definitive-guide/ They basically suggest to avoid cookies but that would leave out SSR on first page load i think.

LightningK0ala commented 7 years ago

Hey everyone!

I'm also working on authentication with next.js and the comments from this issue, in particular @davibe + the repo from @iaincollins and the PR from @timneutkens have been of tremendous help.

My solution does the following:

It helps that I have my own JWT token authentication microservice running in a docker container. Perhaps it would be easier to provide a simple JWT service as part of the with-auth example? Thus avoiding to hack the server.js and potentially losing the benefits of next.js baked-in hot reloading, ssr and routing?

I'm also not sure whether this approach is safe in terms of CSRF / XSS. Any comments are welcome.

Thanks to you all for the amazing work you've done so far. I'm a big fan of this project!

timneutkens commented 7 years ago

@jcsmesquita I'm working on a new version of the example with everything separated from Next.js similar to how auth is implemented at zeit.co.

jacobpdq commented 7 years ago

This seems to be useful: https://hub.docker.com/r/rabbotio/nap/~/dockerfile/

katopz commented 7 years ago

@subsumo Thanks for mention, FYI : NAP is mine, the authen via web is base on @iaincollins nextjs-starter plus react native client login with token.

nickredmark commented 7 years ago

@timneutkens does the solution you are working on allow you to authenticate towards a separate service?

I've been looking at the current auth example pull request https://github.com/zeit/next.js/pull/1141 It seems to me that it only allows to authenticate towards the next.js server, but not isomorphically towards a separate service.

In other words, assume you want to separate the next.js server and the actual app API (e.g. REST or GraphQL), then what you want to do is that the client and the server are able to authenticate towards the API. I don't think this solution really helps you there.

nickredmark commented 7 years ago

I thought of a flow.

Entities:

The goal is to establish 3 cookie-based sessions:

  1. C-S
  2. C-A
  3. S-A

Session 1) is so the Client recognizes the Server, and 2) 3) are so that both Client and Server can use their respective sessions to access the API independently.

This is the flow:

  1. C-S is easily done, no authentication needed
  2. C-A is done with authentication (e.g. with username/password). Additionally, a JWT is provided
  3. Client provides JWT to Server and then discards it
  4. S-A is done with JWT, which is then discarded

What's your opinion? Is there an easier way? Should we instead just keep the API coupled with the next.js server, so that only one session is needed (C-S)?

nickredmark commented 7 years ago

@rauchg I don't summon you easily, but I believe this is also about what direction next.js has to go - as a "universal front-end" should it run separately from the API providing the data? If yes we need to get this right.

timneutkens commented 7 years ago

@timneutkens does the solution you are working on allow you to authenticate towards a separate service?

That's the idea yeah. Been busy fixing other stuff on Next.js. Will get back to it ASAP.

possibilities commented 7 years ago

I broke out github-auth-specific parts of my app into a pretty digestible example. might be interesting for some and would love any feedback on the code or flow: https://github.com/possibilities/next-github-auth-example

UPDATE: I refactored the example app into a reusable set of components that can be "dropped into" next apps

nickredmark commented 7 years ago

Follow-up: I wrote an integration with ooth and a GraphQL API.

https://medium.com/the-ideal-system/ooth-user-accounts-for-node-js-93cfcd28ed1a#.ykoj1dhil

it's based on existing approaches, i.e. authentication towards the next.js server, that is it assumes the api and the authentication server to run all in the same process, so only one session has to be created. The advantage: in principle it can store credentials for / is extensible to virtually any passport.js strategy.

czzarr commented 7 years ago

@timneutkens any progress on that front?

possibilities commented 7 years ago

I refactored my github auth example app into a reusable set of decorators and page component for β€œdropping github auth into” next apps. Code and functionality feedback welcomed. https://github.com/possibilities/next-github-auth

I think it could be interesting to nail down the boards here and then continue evolving this into a more generic auth framework for next.

kolpav commented 7 years ago

@timneutkens Sorry to ping you but have you made any progress on this? I am completely lost on how should I set up authentication with next.js properly.

timneutkens commented 7 years ago

@kolpav I have done some work on it, currently swamped with other stuff πŸ˜₯ By all means this is pretty high on my priorities list for Next πŸ˜„

camstuart commented 7 years ago

A clear, well documented and secure way of achieving authentication with a next.js app is critical to it's success as a framework.

I can't remember the last time I built a web that did not have authentication.
Like most people I imagine, I also want to make secured calls to my backed for rest data, so JWT seems the obvious solution.

But there is so much discussion among the issues and PR's I'm not sure where to start!

kolpav commented 7 years ago

@timneutkens Cool πŸ‘ I think it is gonna be very valuable for others.

iaincollins commented 7 years ago

@camstuart @kolpav There are some good, working examples above including supporting oAuth and email based auth that uses both JWT and HTTP Cookies by contributors @jaredpalmer, @luisrudge, @impronunciable, @possibilities and myself.

To highlight some links work checking out:

(The micro auth example was good but I think it needs updating.)

There is scope for further improvement, which additional commentators have commented on above - including a session store component and splitting the server logic; and an example that simplifies things even further which Tim has been working on.

Simplicity for auth is a challenging area for Universal apps, but you should be able to get the above examples going - or just try out the demos directly - and see how they work without too much fuss.

salmazov commented 7 years ago

@iaincollins That's great examples. But how can I use (for example) starter project? So If I want to build my app. I need to clone this repo? Or I need to "copy-paste" code from starter project chunk by chunk to my own code?

If starter project will be updated - what I should do?

kolpav commented 7 years ago

@iaincollins Nice examples especially yours. But still, I would like to see auth example with zeit seal of approval on it πŸ˜„ all eyes would point into one direction so any errors or mistakes would not go unnoticed. For now, I have my own working auth but I am not sure how secure it is.

camstuart commented 7 years ago

Agreed @kolpav, rolling your own security is a tricky business. Best left to the experts

XavierGeerinck commented 7 years ago

I have created a stack for this that can handle authentication easily with GraphQL: https://github.com/thebillkidy/MERGE-Stack

iaincollins commented 7 years ago

@salmazov I've found a useful way to get started and understand what's going on in an example or starter project can be to fork it, then strip out things that aren't relevant until you are left with just the code related to the functionality you want to implement; and then to try porting just that functionality over to over to another project.

@kolpav @camstuart This thread contains really extensive discussion of various security models, including debunking some common misconceptions and trade-offs. I'd particularly note the points about HTTP Only Cookies and CSRF tokens (and the additional protection they give against XSS and CSRF over using JWT and/or the Web Storage API for session tokens). It's really worth a read.

kolpav commented 7 years ago

@iaincollins Did you meant to link something? :smile:

kamilml commented 7 years ago

Hello guys :)

I have a question, I was reading next can't get the tokens for auth with jwt from localstorage.

If I have a server for render just the first charge of my site with next. And I have another server for my api. This receive the user/pass from the client and give a jwt. In which cases I need to get the token in the server??

Why would I need token in the render server (next)?

If the client dont send the token to the api server, the api dont give the data, and the user can't get private info. I'm dont understanding why I need to send the token to the render server.

eezing commented 7 years ago

@kamilml

This might help. In general, you have two options:

  1. Give Next server JWT (possibly via cookie). This will allow Next server to make api calls on the client's behalf. You would want this if full server side rendering is important to you.

  2. Store JWT in client local storage and don't give Next server access to it. In this case, you can simply bypass the api calls server side and postpone full render until client side loading is complete.

bjunc commented 6 years ago

Apologies for re-opening this, but I thought I would add my 2 cents to this thread, and how my initial R&D is panning out in this area. Less code examples, more high-level flow.

First, for context, most of our app is already built in Symfony 3 (PHP), and uses Vue for a hybrid experience. The server renders a wrapper page, and assigns app data to __INITIAL_STATE__ for the app to pick up. This has resulted in making a decision between rendering marketing pages in Symfony (for SEO), and choosing UX/UI over SEO in other areas by fetching data via JS and providing a more SPA-ish feel. Also worth noting, not all pages are a binary public/private (as I've seen in some examples). Some pages are public by default, and then render differently if authenticated. We were considering using an SPA for parts of the site, but in many practical ways, it was a worse UX/UI (slower TTI, progress bars, etc.). Plus, that doesn't solve the SEO problem, unless we introduce FOUC and render text twice (once through Symfony, once again as JS components), etc..

Enter SSR / Universal JS...

In my case, what I did, was to mimic the PHPSESSID logic, by creating an HttpOnly UJSSESSID cookie (made the name up), and set the value to the user's JWT. The Symfony app passes this along in each page request as the user navigates around the site. When the user hits the UJS pages, the server-side of those apps will receive the cookies in the request (per the browser's built-in behavior). If the UJSSESSID cookie is set, the app calls the API to get the user information (eg. /api/v1/users/me with passing the token via Authentication header). The rest of the calls are done through the API, using the same token. The Symfony logout mechanism clears the UJSSESSID cookie. The next time the UJS app loads, it will render the pages in anonymous user mode. FYI, the Symfony vs. UJS page routing is done through Apache's ProxyPass. LocalStorage isn't used.

The end result is a seamless UX where the user is on some pages that are PHP with client-side JS, and some pages that are UJS. This enables us to do A/B tests, and iteratively update the site – not everyone gets to start from scratch :)

While this is a bit more complicated due to the symbiotic PHP/UJS, the same principle can be used in a full UJS solution with an API or Node.js server middleware (eg. Express, Adonis, etc.). Instead of setting the UJSSESSID cookie via a PHP page request (HttpOnly flag), have the user login through your SPA/UJS, and set the cookie there. What you SHOULD NOT do, is use your app to decode the JWT, or make 3rd party calls that require a client_secret. Use a middleware that stays on the server for that.

Hope that helps someone. Other examples I've seen were a little too petri dish for me.

supra28 commented 6 years ago

@jaredpalmer Hey thanks for that implementation , I tried it , just copy-pasted all your code replaced your dashboard.js with my index.js which looks like this:

const index = () =>
  <div>
    <span>WoooHoooo</span>
  </div>

export default withAuth(index)

and in withAuth hoc i changed it to redirect to the signin page. But before it redirects to the sign in page the contents of the index page still flash for a bit. :S

gielcobben commented 6 years ago

What's the status of this issue? πŸ˜‡

trandainhan commented 6 years ago

This is bit overwhelm for new people reading through the whole discussion. I decided to implement the very basic authentication at here. Only 2 pages (index, login), and a custom server https://github.com/trandainhan/next.js-example-authentication-with-jwt

Basically, we have a authentication middleware in server to check token in header of every request. The jwt-token will be store in cookies. I find this is very simple, straight forward and work very well.

sbking commented 6 years ago

@trandainhan Could you add a POST endpoint that uses a secret token to prevent CSRF attacks?

trandainhan commented 6 years ago

@sbking Updated source code with an example endpoint protected by CSRF attacks

gielcobben commented 6 years ago

Is this ready to use 😬?

maticzav commented 6 years ago

Anyone tried auth with redux-auth-wrapper?

nickredmark commented 6 years ago

Hello everybody! Over the last months I created, and now refined

It features:

Check out a live demo here: http://staart.nmr.io/

I did it mainly for myself for rapid prototyping, to get started quickly with an application with a simple, working accounts system that doesn't rely on external services. I'm quite happy with the results. I intend to keep using and maintaining these libraries, so give it a go if you feel that an established accounts system + UI for node is still missing.