Giter Site home page Giter Site logo

nevernull / gatsby-apollo-wpgraphql-jwt-starter Goto Github PK

View Code? Open in Web Editor NEW
27.0 5.0 6.0 1.5 MB

A gatsby starter, that should serve as an entry point for user authentication with WPGraphQL, JWT, Apollo and private routes.

License: MIT License

JavaScript 67.70% CSS 32.30%
gatsby wpgraphql jwt graphql apollographql

gatsby-apollo-wpgraphql-jwt-starter's Introduction

Gatsby Apollo WPGraphQL JWT Starter

This project aims to serve as a good starting point, to handle user registration and login with Apollo, WPGraphQL and WPGraphQL JWT Authentication.

We gonna use the following libraries for now:

It should work now with the latest versions of WPGraphQL. Apollo v3 might change from beta to a stable release.

πŸš€ Quick start

WordPress

  1. Install plugins

    Download the .zip files and install through the WordPress Admin or if you can run git, just run the following commands inside your ./wp-content/plugins/ folder:

    git clone https://github.com/wp-graphql/wp-graphql.git
    git clone https://github.com/wp-graphql/wp-graphql-jwt-authentication.git
    
  2. Check your permalinks

    Make sure your graphql endpoint works as expected. See these docs: https://docs.wpgraphql.com/getting-started/install-and-activate/#verify-the-endpoint-works

  3. Define a secret In your wp-config.php deinfe a secret. You can use WordPress Salt generator (https://api.wordpress.org/secret-key/1.1/salt/) to generate a Secret.

    define( 'GRAPHQL_JWT_AUTH_SECRET_KEY', 'your-secret-token' );
    

Gatsby

  1. Install modules

    Run yarn to install packages. Also after the modules are installed it should run createPossibleType.js automatically on postinstall.

    yarn

    Check if the file ./apollo/possibleTypes.json has been created.

  2. Add .env.development

    There is an .env.development.example which you can use and rename. Make sure you have a .env.development in your root folder.

    GRAPHQL_URL=http://your-domain/graphql

    If you run yarn run build you need a .env.production in you root folder. Or you run it in your CI wit CI-Variables.

  3. Start developing

    Navigate into your new site’s directory and start it up.

    yarn run develop

    or

    yarn run cdev

    cdev runs gatsby clean before running develop

  4. Open the source code and start editing!

    Your site is now running at http://localhost:8000!

    Note: You'll also see a second link: http://localhost:8000/___graphql. This is a tool you can use to experiment with querying your data. Learn more about using this tool in the Gatsby tutorial.

    Open the my-default-starter directory in your code editor of choice and edit src/pages/index.js. Save your changes and the browser will update in real time!

gatsby-apollo-wpgraphql-jwt-starter's People

Contributors

henrikwirth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

gatsby-apollo-wpgraphql-jwt-starter's Issues

gatsby build fails due to missing isBrowser check on isLoggedIn() method

Hi all,

First of all, thank you so much for creating this starter as it is super helpful in establishing best practices on how to interact with authentication on WPGraphQL.

I noticed that when attempting to run the build script, the following error occurs:

  47 | 
  48 |   const isLoggedIn = () =>
> 49 |     getInMemoryAuthToken().authToken && !isTokenExpired(getInMemoryAuthToken().authToken)
     |                            ^
  50 | 
  51 |   useEffect(() => {
  52 |     if (isOnline && getRefreshToken()) {


  WebpackError: TypeError: Cannot read property 'authToken' of null
  
  - useAuth.js:49 Object.isLoggedIn
    src/hooks/useAuth.js:49:28
  
  - dashboard.js:23 Dashboard
    src/pages/dashboard.js:23:13

It appears that this can be remedied by importing isBrowser into useAuth.js and then making the following modification to the isLoggedIn method:

  const isLoggedIn = () =>
    isBrowser &&
    getInMemoryAuthToken().authToken &&
    !isTokenExpired(getInMemoryAuthToken().authToken)

This is also inline with how the gatsby tutorial approaches it:

export const isBrowser = () => typeof window !== "undefined"
export const getUser = () =>
  isBrowser() && window.localStorage.getItem("gatsbyUser")
    ? JSON.parse(window.localStorage.getItem("gatsbyUser"))
    : {}

https://www.gatsbyjs.org/tutorial/authentication-tutorial/

Epic

Intro

This epic is to track possible solutions on how to implement the JWT User Authentication workflow.

Philosophy

Checkout this post: https://blog.hasura.io/best-practices-of-using-jwt-with-graphql

I want to use that post as a base for the implementation.

Persisting JWT token in localstorage (prone to XSS) < Persisting JWT token in an HttpOnly cookie (prone to CSRF, a little bit better for XSS) < Persisting refresh token in an HttpOnly cookie (safe from CSRF, a little bit better for XSS).

Note that while this method is not resilient to serious XSS attacks, coupled with the usual XSS mitigation techniques, an HttpOnly cookie is a recommended way persisting session related information. But by persisting our session indirectly via a refresh token, we prevent a direct CSRF vulnerability we would have had with a JWT token.

Here is also a good reference: https://flaviocopes.com/graphql-auth-apollo-jwt-cookies/

Auth Flow Strategy

Register/SignUp

  1. User registers through from with following mutation:

    mutation RegisterUser($input: RegisterUserInput!) {
         registerUser(input: $input) {
             user {
                 jwtAuthToken
                 jwtRefreshToken
             }
         }
     }
    
  2. Same as Login 2. - 6.

Login

  1. The user logs in with
    mutation LoginUser($input: LoginInput!) {
        login(input: $input) {
            user {
                jwtAuthToken
                jwtRefreshToken
            }
        }
    }
    
  2. WordPress generates new jwtAuthToken (5min Expiration) and jwtRefreshToken (long lived: how long?)
  3. jwtAuthToken and jwtRefreshToken are returned back to the client as a JSON payload.
  4. The jwtAuthToken is stored in memory, the jwtRefreshToken in localStorage.
  5. A countdown to a future silent refresh is started based on jwtAuthExpiration

On Page Visit

  1. Check if jwtAuthToken is in Memory, if not check if jwtRefreshToken is stored in localStorage
  2. If jwtRefreshToken is stored, then start Silent Refresh to get new jwtAuthToken to store it in Memory

Silent Refresh

  1. Call refresh mutation like:
    mutation($input: RefreshJwtAuthTokenInput!) {
      refreshJwtAuthToken(input: $input) {
        authToken
      }
    }
    
    Note: There is no new jwtAuthExpiration and isJwtAuthSecretRevoked given and authToken is not called jwtAuthToken => Issue for JWT plugin
  2. Server checks jwtRefreshToken and if it is valid (or hasn't been revoked) jwtRefreshToken
    • Then: the server returns a new jwtAuthToken and jwtAuthExpiration to the client and also sets a new jwtRefreshToken cookie via Set-Cookie header.
  3. Use jwtAuthToken in Payload and save in Memory.
  • Before any request to the server, Expiration could be checked and a refresh could be triggered, if the jwtAuthToken is expired.

Scope

Starter

  • Setup initial Project with current Apollo v3
  • Login Form
  • SignUp Form
  • Protected Routes (Dashboard)
  • RegisterUser
  • LoginUser
  • Auth Token only in Memory
  • Make sure, everything works with WPGraphQL v0.6.x
    - There are some issues, see: wp-graphql/wp-graphql-jwt-authentication#69
  • Silent Refresh
  • Error Handling for Invalid Tokens or User Credentials
  • Ability to force logout all sessions (invalidate all refresh tokens assigned to user)
  • Is there a way to only invalidate one session? So one refresh token assigned to a certain client, that can be revoked???
  • Set Refresh Token Cookie on Server response HttpOnly
    • This is being discussed and probably it doesn't make a difference if it is in the cookie or localStorage

NPM Module

The idea is to use all the implemented above and abstract it in a way, it is easy for users to plugin to their projects, with less boilerplate.

  • Setup initial project
  • Define API user can interact with
    • AuthProvider: A Provider, that the user should use in wrapRootElement
    • useAuthContext: Context hook, that can be used to access everything living in the Context
    • login(redirect?)
    • logout(redirect?)
    • isLoggedIn
    • PrivateRoute Component

LoginInput Issue

First off, awesome job on this repo.

When attempting to login i get the following error: Type loader is expected to return a callable or valid type \"LoginInput\", but it returned null

What is causing this/how can I resolve?

Error: GraphQL error: The JWT token could not be returned

I'm not sure why the mutation is looking like this, Where is LoginInput! coming from?

    mutation LoginUser($input: LoginInput!) {
        login(input: $input) {
            user {
                jwtAuthToken
                jwtRefreshToken
                jwtAuthExpiration
                isJwtAuthSecretRevoked
                username
                nicename
            }
        }
    }

From the wpgraphql-jwt docs, I see authToken is used but here in this Boilerplate not

  login( input: {
    clientMutationId:"uniqueId"
    username: "your_login"
    password: "your password"
  } ) {
    authToken
    user {
      id
      name
    }
  }
}

If anyone can clarify this to me would be very thankful. Also, I hope to create a .env.development and putting my GRAPHQL_URL=https://mysite.com/graphql is the correct way to do it in Gatsby.

But I copied the Logic of this boilerplate into a CRA/Apollo app and I'm still not able to get a token.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.