1
votes

I'm new to apollo/graphql and I'm trying to get my authentication done properly in a greenfield project. My authentication provider is AWS cognito. I wrote a cognito helper module to interact with it.

Though I'm not quite sure how to sync my apollo client with my auth state.

export const authenticate = (username: string, password: string) => {
  const authDetails = new AuthenticationDetails({
    Username: username,
    Password: password,
  })
  const cognitoUser = getCognitoUser(username)
  return new Promise((resolve, reject) => {
    cognitoUser.authenticateUser(authDetails, {
      onSuccess: result => {
        resolve(result)
      },
      onFailure: err => {
        reject(err)
      },
    })
  })
}

export const getCurrentUserToken = () => {
  return new Promise((resolve, reject) => {
    const currentUser = userPool.getCurrentUser()
    if (currentUser) {
      currentUser.getSession((error, session) => {
        if (error) {
          reject(error)
        }
        resolve(session.getIdToken().getJwtToken())
      })
    } else {
      resolve(null)
    }
  })
}

export const logout = () => {
  const currentUser = userPool.getCurrentUser()
  if (currentUser) {
    currentUser.signOut()
  }
}

Right now I'm just using these function to handle my login by calling them in my react component handlers. I configured an apollo-link for adding the auth header. Inject my JWT token data into context at the backend and implemented a currentUser query resolver in the backend.

const resolvers = {
  RootQuery: {
    currentUser: (obj, args, context) =>
      context.tokenData
        ? {
            id: context.tokenData.sub,
            name: context.tokenData.name,
            email: context.tokenData.email,
            username: context.tokenData['cognito:username'],
          }
        : null,
  },
}

In my react App layout i got a component UserPanel which queries that currentUser query.

const CURRENT_USER_QUERY = gql`
  query {
    currentUser {
      name
    }
  }
`

export default graphql(CURRENT_USER_QUERY)(UserPanel)

When i am logging in now obviously the UserPanel does not update its currentUser query except I'm reloading the page ofc. Though im also having troubles finding a good solution to sync them.

I was thinking about implementing my login via graphql mutation using apollo-link-state to do it locally and watch these to refetch if someone logged in/out. I'm not sure if this is fine since it seems to me that this link cannot resolve async stuff (e.g. promises) in its mutation resolvers.

Another option I was thinking about was to decouple the auth process from the apollo client completely and implement some auth pubsub system maybe with Observables and let the react components refetch the queries if the authentication state changes.

I'm very uncertain how to continue and every solution I'm thinking about doesn't feel like the recommended way to go.

1
what have you done? - user4412054

1 Answers

0
votes

I don't have the full picture with regards to your React setup but here I go. It might be that Apollo-client is caching CURRENT_USER_QUERY locally and is showing you the results of a previous query. You could try the network-only option on the query:

export default graphql(CURRENT_USER_QUERY, { options: {fetchPolicy: 'network-only' }})(UserPanel)

What I have in React is an AppContainer which is my parent component. It checks if the user is logged in:

const loggedInUser = gql`
  query loggedInUser{
    user {
      id
      role
    }
  }`


export default graphql(loggedInUser, { options: {fetchPolicy: 'network-only' }})(AppContainer)

Then on my UserProfile page, I use a data container to fetch the data before passing it down to the UserProfile child component. I think the loggedInUser query automatically updates the user in the apollo store. With it apollo-client realizes that it needs to refetch userQuery. Does that help?

const userQuery = gql`
  query userQuery {
    user {
      id
      name
      email
      role
      company
    }
  }
`
export default graphql(userQuery, {name: 'userQuery'})(UserDataContainer);