lvarayut / relay-fullstack

:point_up::running: Modern Relay Starter Kit - Integrated with Relay, GraphQL, Express, ES6/ES7, JSX, Webpack, Babel, Material Design Lite, and PostCSS
https://lvarayut.github.io/relay-fullstack/
MIT License
986 stars 126 forks source link

Implement JSON Web Tokens (JWT) #42

Open lvarayut opened 8 years ago

lvarayut commented 8 years ago

Implement Signup and Login pages

ncrmro commented 7 years ago

Thinking how we can flesh out the auth flow process a bit while still using a static backend.

Components

navbar

jwt token logic

Client

Server

Authenticate JWT Token

Generate Refresh Tokens

Authenticated Routes

Neitsch commented 7 years ago

I mostly agree.

The best starting point is probably assigning a JWT token on a login mutation, for now, not considering username/password at all.

ncrmro commented 7 years ago

@Neitsch I've used this post as guidance atm

http://stackoverflow.com/questions/32535141/relayjs-authentication-using-relay-which-mutation-to-use

The viewer object always has the same viewer id, but with the token the viewer object then has a user object. Feels very hackish.

Looking to see if relay 2 makes this easier.

Edit: @Neitsch also does everything still work if the viewer is null?

Neitsch commented 7 years ago

Good point. So, how I see it is, that the viewer query is without arguments, so no ID or anything. It returns the user mapped to that token, which then has the ID of the viewer assigned. The root query does not contain any arguments concerning the user ID, that should all be done on the backend: If I query viewer { username, id }, it will return { username: 'Neitsch', id: 1234 }, but maybe I'm just at a misunderstanding about JWT. Looking at the guide, the resolve function would be something like this:

var GraphQLRoot = new GraphQLObjectType({
  user: {
  type: new GraphQLNonNull(GraphQLUser),
  description: 'the user',
  resolve: (root, {id}, {rootValue}, context) => co(function*() {
    var user = yield getUser(context.request.cookies.token);
    return user;
  })
}

About null, actually you're right. Null is never good. Instead the IMHO proper way is to have a viewer interface, that either implements a logged in or a logged out user.

ncrmro commented 7 years ago

@Neitsch Yes thats my understanding as well.

The server reads the JWT Tokens Payload. It should be a valid token. The payload has a userID and sense it's signed and not expired we can trust it's validity

This medium post is usefull. GraphQL Field Guide to Auth

Neitsch commented 7 years ago

Ah okay, I read up on it a little bit. My understanding of JWT was wrong. That Medium post looks good, doesn't use Relay though, so we might have to play with that, if we follow their strategy for authorization. The authentication code lgtm.

ncrmro commented 7 years ago

This also has a really nice pattern that has the jwt token in the graphql query GraphQL and Authentication

I actually like this option a lot

Neitsch commented 7 years ago

This was the concept I was thinking about above. I know how to do this with cookies. Not sure if JWT changes anything 😄

ncrmro commented 7 years ago

I have a lot of the client code here

Name isAuthenticated and Authenticated Route higher order components in utils.js.

Some logic in regards to checking, setting and handling expired jwt tokens is in jwtUtils file.

lvarayut commented 7 years ago

That's looking very good to me 💯. It would be great if you could integrate the code into Relay Fullstack

ncrmro commented 7 years ago

@lvarayut definitely going to try to look into this the next few days.