First let me mention that this is for a public facing website that will only have two users where everything requires authentication. There should be no risk of one of us submitting malicious form data for stealing tokens via XSS.After days of research into various topics (JWT vs sessions) and libraries (Passport) I've come to the conclusion that I was probably making things too complicated. I will have a GraphQL back-end built with graphql-express and a Vue front-end. There are three ways a person can be authenticated:• Username and password • OAuth/social login • API keyI started with Passport and specific strategies for all of those authentication methods. Then I realized I didn't need the local strategy because after passing my username and password to a GraphQL mutation, I would receive a JWT back and then the JWT strategy is all that would be needed. Same thing for OAuth, really. Back-end confirms I am who I say I am and returns a JWT.Then it dawned on me further, I don't need a passport strategy for pulling a JWT out of a header, just a simple middleware that determines the user and sets them as req.user. A few lines of JavaScript and I can pull the Authorization header which will be either a JWT or an API key. At this point I realized I probably don't even need Passport.All that said, I'm new to server-side JavaScript and am hoping someone can give this authentication flow a second check.First possibility of username and password. Enter credentials into front-end form, post to GraphQL mutation, receive JWT back if authenticated. That JWT is stored in local storage and sent with all future requests.Second possibility of OAuth. Front-end form authenticates with 3rd party and receives an access token, that token is sent to the back-end via mutation, back-end sends that token to 3rd party in exchange for user data, if that user data exists in the local database a JWT is returned. That JWT is stored in local storage and sent with all future requests.Third possibility of API key. An authenticated user generates an API key for use with command-line API requests. Requests simply include that key in the Authorization header just like a JWT, only it's a random string, not a meaningful payload.I'm not worried about being a stateless purist and would have no problem using cookies except A) a proper cookie shouldn't be readable by JavaScript so my application wouldn't be able to tell I was logged in on page load unless I used a dedicated "me" query and B) returning a JWT from a login mutation for setting in local storage honestly seemed easier than dealing with cookies (can you even test in GraphQL Playground using cookie authentication?).Lastly, since JWTs have a set expiration and I would like to stay logged in as long as I'm continuously using the front-end, I would like a way to keep pushing the token expiration into the future. Rather than using a separate refresh token, I thought I would just make a request to a "refresh" mutation that would send me a new token. Similar to receiving a token upon logging in but in this case using my existing, still-valid token as credentials, basically.I hope I'm not too far off. Would love a second opinion while keeping in mind this site has two users, neither of whom have malicious intent to steal tokens or inject JavaScript for XSS attacks (but even if we did we could just sanitize our inputs).
Submitted May 05, 2020 at 09:17AM by justinvoelker
No comments:
Post a Comment