13
votes

I am currently handling authentication outside of relay with custom header set in DefaultNetworkLayer. Is it preferred way and Is there a way to do it in relay? I was stuck when trying to implement sign up functionality in relay: in relay there are following configs: FIELDS_CHANGE, NODE_DELETE, RANGE_ADD, RANGE_DELETE. So RANGE_ADD is the only one applicable here, however I need a parent and connection for that, which I do not have for a newly created user....

3

3 Answers

16
votes

I had this issue on my project as well, here is how I handled it. I did a User Schema with that fields

export var GraphQLUser = new GraphQLObjectType({
  name: 'User',
  fields: {
  id: globalIdField('User'), //this id is an immutable string that never change.
  userID: {
    type: GraphQLString,
    description: 'the database user\'s id',
  },
  name: {
    type: GraphQLString,
    description: 'the name of the user',
  },
  mail: {
    type: GraphQLString,
    description: 'the mail of the user',
  }
})

then here is how my user field on my Root schema looks like

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

on your Root schema you ask for the getUser function that I implemented as follows, here is the main hack!

const logID = 'qldfjbe2434RZRFeerg'; // random logID that will  remain the same forever for any user logged in, this is the id I use for my FIELD_CHANGE mutation client side

export function getUser(rootValue) {
  //IF there is no userID cookie field, no-one is logged in
  if (!rootValue.cookies.get('userID')) return {
    name: '',
    mail: '',
    id: logID
  };
  return co(function*() {
    var user =  yield getDatabaseUserByID(rootValue.cookies.get('userID'));
    user.userID = user.id;
    user.id = logID // change the id field with default immutable logID to handle FIELD_CHANGE mutation
    return user;
  })
}

here is the loginMutation client Side

export default class LoginMutation extends Relay.Mutation {
  static fragments = {
    user: () => Relay.QL`
     fragment on User {
      id,
      mail
    }`,
  }
  getMutation() {
    return Relay.QL`mutation{Login}`;
  }

  getVariables() {
    return {
      mail: this.props.credentials.pseudo,
      password: this.props.credentials.password,
      id: this.props.user.id
    };
  }
  getConfigs() {
    return [{
      type: 'FIELDS_CHANGE',
      fieldIDs: {
        user: this.props.user.id,
      }
    }];
  }
  getOptimisticResponse() {
    return {
      mail: this.props.credentials.pseudo,
      id: this.props.user.id
    };
  }
  getFatQuery() {
    return Relay.QL`
    fragment on LoginPayload {
      user {
        userID,
        mail,
        name,
      }
    }
    `;
  }

then here is the loginMutation server Side

export var LoginMutation = mutationWithClientMutationId({
  name: 'Login',
  inputFields: {
    mail: {
      type: new GraphQLNonNull(GraphQLString)
    },
    password: {
      type: new GraphQLNonNull(GraphQLString)
     },
     id: {
       type: new GraphQLNonNull(GraphQLString)
    }
  },
  outputFields: {
    user: {
      type: GraphQLUser,
      resolve: (newUser) => newUser
    }
  },
  mutateAndGetPayload: (credentials, {rootValue}) => co(function*() {
    var newUser = yield getUserByCredentials(credentials, rootValue);
    //don't forget to fill your cookie with the new userID (database id)
    rootValue.cookies.set('userID', user.userID);
    return newUser;
  })
});

Et voilà! To answer your question I used the FIELD_CHANGE mutation and shared the same id doesnt matter which user is logged in, the real id used to get the right user is actually userID. It works fine on my project. You can have a look on it here Relay-Graphql-repo

PS: You'll have to add it on your networkLayer to accept cookies

Relay.injectNetworkLayer(
  new Relay.DefaultNetworkLayer('/graphql', {
  credentials: 'same-origin'
  })
);
1
votes

Relay is intentionally agnostic about authentication mechanisms, so using a custom header via the network layer is appropriate.

As for how to handle the post-login response, it's probably best to trigger a full page refresh or a redirect at that point, because Relay doesn't currently have a way to reset all internal state (although it is on the list of desirable items). Given that viewer identity can so drastically affect item visibility, a full reset is pretty much required in order to maintain correct/robust behavior.

0
votes

There exists an undocumented config type, which looks right for that use case: https://github.com/facebook/relay/issues/237