Skip to content

Commit

Permalink
Add graphql-local and facebook strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
jkettmann committed Aug 2, 2019
1 parent f3330c4 commit 2f1a725
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 12 deletions.
7 changes: 6 additions & 1 deletion .gitignore
Expand Up @@ -23,4 +23,9 @@ yarn-debug.log*
yarn-error.log*

# editor
.vscode
.vscode

# environment variables
.env

post.md
29 changes: 28 additions & 1 deletion README.md
Expand Up @@ -23,6 +23,33 @@ npm start
Visit [http://localhost:4000/graphql](http://localhost:4000/graphql). You will see the Apollo playground. There you can run following query and mutation

```graphql
mutation {
login(email: "maurice@moss.com", password: "abcdefg") {
user {
id
firstName
lastName
email
}
}
}

mutation {
signup(
firstName: "Jen",
lastName: "Barber",
email: "jen@barber.com",
password: "qwerty"
) {
user {
id
firstName
lastName
email
}
}
}

query {
currentUser {
id
Expand All @@ -35,4 +62,4 @@ query {
mutation {
logout
}
```
```
55 changes: 51 additions & 4 deletions api/index.js
@@ -1,7 +1,10 @@
import 'dotenv/config';
import express from 'express';
import session from 'express-session';
import uuid from 'uuid/v4';
import passport from 'passport';
import FacebookStrategy from 'passport-facebook';
import { GraphQLLocalStrategy, buildContext } from 'graphql-passport';
import { ApolloServer } from 'apollo-server-express';
import User from './User';
import typeDefs from './typeDefs';
Expand All @@ -10,6 +13,47 @@ import resolvers from './resolvers';
const PORT = 4000;
const SESSION_SECRECT = 'bad secret';

const facebookOptions = {
clientID: process.env.FACEBOOK_APP_ID,
clientSecret: process.env.FACEBOOK_APP_SECRET,
callbackURL: 'http://localhost:4000/auth/facebook/callback',
profileFields: ['id', 'email', 'first_name', 'last_name'],
};

const facebookCallback = (accessToken, refreshToken, profile, done) => {
const users = User.getUsers();
const matchingUser = users.find(user => user.facebookId === profile.id);

if (matchingUser) {
done(null, matchingUser);
return;
}

const newUser = {
id: uuid(),
facebookId: profile.id,
firstName: profile.name.givenName,
lastName: profile.name.familyName,
email: profile.emails && profile.emails[0] && profile.emails[0].value,
};
users.push(newUser);
done(null, newUser);
};

passport.use(new FacebookStrategy(
facebookOptions,
facebookCallback,
));

passport.use(
new GraphQLLocalStrategy((email, password, done) => {
const users = User.getUsers();
const matchingUser = users.find(user => email === user.email && password === user.password);
const error = matchingUser ? null : new Error('no matching user');
done(error, matchingUser);
}),
);

passport.serializeUser((user, done) => {
done(null, user.id);
});
Expand All @@ -32,13 +76,16 @@ app.use(session({
app.use(passport.initialize());
app.use(passport.session());

app.get('/auth/facebook', passport.authenticate('facebook', { scope: ['email'] }));
app.get('/auth/facebook/callback', passport.authenticate('facebook', {
successRedirect: 'http://localhost:4000/graphql',
failureRedirect: 'http://localhost:4000/graphql',
}));

const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({
user: req.user,
logout: () => req.logout(),
}),
context: ({ req, res }) => buildContext({ req, res, User }),
playground: {
settings: {
'request.credentials': 'same-origin',
Expand Down
29 changes: 29 additions & 0 deletions api/resolvers.js
@@ -1,8 +1,37 @@
import uuid from 'uuid/v4';

const resolvers = {
Query: {
currentUser: (parent, args, context) => context.user,
},
Mutation: {
signup: (parent, { firstName, lastName, email, password }, context) => {
const existingUsers = context.User.getUsers();
const userWithEmailAlreadyExists = !!existingUsers.find(user => user.email === email);

if (userWithEmailAlreadyExists) {
throw new Error('User with email already exists');
}

const newUser = {
id: uuid(),
firstName,
lastName,
email,
password,
};

context.User.addUser(newUser);

context.login(newUser);

return { user: newUser };
},
login: async (parent, { email, password }, context) => {
const { user } = await context.authenticate('graphql-local', { email, password });
context.login(user);
return { user }
},
logout: (parent, args, context) => context.logout(),
},
};
Expand Down
6 changes: 6 additions & 0 deletions api/typeDefs.js
Expand Up @@ -12,7 +12,13 @@ const typeDefs = gql`
currentUser: User
}
type AuthPayload {
user: User
}
type Mutation {
signup(firstName: String!, lastName: String!, email: String!, password: String!): AuthPayload
login(email: String!, password: String!): AuthPayload
logout: Boolean
}
`;
Expand Down
90 changes: 87 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions package.json
@@ -1,7 +1,7 @@
{
"name": "authentication-with-graphql-passport-and-react-starter",
"name": "frontend-for-authentication-with-graphql-and-passport",
"version": "0.1.0",
"description": "Starter project accompanying a series of articles on authentication with GraphQL",
"description": "Frontend for tutorial about authentication with GraphQL and passport",
"keywords": [
"graphql",
"auth",
Expand All @@ -15,16 +15,19 @@
},
"repository": {
"type": "git",
"url": "git://github.com/jkettmann/authentication-with-graphql-passport-and-react-starter.git"
"url": "git@github.com:jkettmann/frontend-for-authentication-with-graphql-and-passport.git"
},
"license": "MIT",
"dependencies": {
"apollo-server-express": "^2.6.3",
"dotenv": "^8.0.0",
"express": "^4.17.1",
"express-session": "^1.16.2",
"graphql": "^14.3.1",
"graphql-passport": "^0.1.5",
"nodemon": "^1.19.1",
"passport": "^0.4.0",
"passport-facebook": "^3.0.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "3.0.1",
Expand Down

0 comments on commit 2f1a725

Please sign in to comment.