Save Token in local Storage using node

前端 未结 2 1205
梦毁少年i
梦毁少年i 2021-02-04 17:59

I\'m using JWT (\"jsonwebtoken\": \"^5.4.0\") with express 4 and jade. I\'m able to create the right Token, but How can i Pass this token in each call? Where I have

2条回答
  •  傲寒
    傲寒 (楼主)
    2021-02-04 18:15

    I would recommend checking this out if you want local storage: https://www.npmjs.com/package/node-localstorage

    But, with that said, you guys and girls wouldn't believe how long it took me to find res.cookie('auth' token) from the above answer. I scoured Google for hours, Passport docs, Express docs, GraphQL and authentication/authorization docs in an effort to find out how to get the token to the API in a stateless manner.

    I already built JWT token security and secured my GraphQL resolvers with it, but then, I opted to use EJS along with graphql-request (approx same as Apollo Client), so I needed to find a way to pass the token to my middleware without using a server side session.

    Storing a JWT token in cookies is fine especially if you take extra precautions such as signing the cookie, and I recall there are also options you can include that keep the cookie secure, so that other sites cannot see it if the "browser" allows access to cookies. If a cookie is signed with your server secret, the data inside the cookie simply cannot be altered and still be valid. The risk is always still someone leaking their token/cookie, and if that bothers you, do research into refresh tokens. However, API tokens are generally and should be kept tightly secret and safe. Your biggest annoyance will more likely be the requirement to maintain a blacklist of JWTs that expire a year from now if you set expiry to 1y.

    I am just including my findings here because this question is actually a rare resource it seems...

    Here is my Express middleware for authentication:

     // AUTHENTICATION
     app.use(async (req) => {
         try {
             const token = req.headers.authorization || req.cookies.auth
             const { person } = await jwt.verify(token, SECRET)
             req.person = person
             return req.next()
         } catch (e) {
             return req.next()
         }
     })
    
    1. You can see I am setting the token from the header with cookie as fallback. This supports my needs fine and allows me to use really any client with stateless security.
    2. My logged in user is available as req.person in my views and GraphQL resolvers. If req.person is not set, the user is treated as not-logged-in.
    3. I am using return req.next() which is important to note because calling next() without parameters is treated as "clean go-to next middleware and/or proceed to process request". If you include any string or object parameter, it will throw an error that can bubble down to your error handling middleware. You can try it yourself. Put return next('You are not authenticated.') in the catch block and you will see it halt the request before your route.
    4. I use return next() because I handle authorization in the routes and in my resolvers. It allows more flexibility such as facilitating register and login mutations to be accessed by non-authenticated users.

    Here is my GraphQL endpoint (I am using Apollo Server):

     app.use('/graphql', bodyParser.json(), graphqlExpress((req) => {
         const context = {
             person: req.person
         }
         return {
             schema,
             context,
             rootValue: null
         }
     }))
    
    1. In my GraphQL resolvers, the third parameter of every query has context.person populated with req.person which comes from the above Authentication middleware.
    2. That is really all a person needs to know.

    Here is how I am using the NPM package called graphql-request: https://www.npmjs.com/package/graphql-request

     app.get('/allpeople', async (req, res) => {
         try {
             const client = new GraphQLClient(GRAPHQL_ENDPOINT, {
                 headers: { Authorization: req.headers.authorization || req.cookies.auth }
             })
             const query = `query allPeople($serialNumber: String!) {
                 allPeople(serialNumber: $serialNumber) {
                     id
                     created
                     status
                     email
                 }
             }`
             const variables = {
                 serialNumber: req.person
             }
             const response = await client.request(query, variables)
             res.render('allpeople/list', { people: response.allPeople })
         } catch (e) {
             throw [`allPeople`, `${JSON.stringify(error, null, 2)}`]
         }
     })
    
    1. I include this code because there are no "more advanced" example usages of graphql-request, and I like it so far. It is very concise and could easily be swapped out for Apollo Client if you venture into React.js. My examples here are also very relevant for anyone researching createNetworkInterface and new ApolloClient().

提交回复
热议问题