Skip to content

Commit

Permalink
Cleanup auth flow
Browse files Browse the repository at this point in the history
  • Loading branch information
lifeiscontent committed Jan 14, 2020
1 parent cf922d4 commit 5dfc446
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 119 deletions.
25 changes: 5 additions & 20 deletions examples/api-routes-apollo-server-and-client-auth/apollo/client.js
Expand Up @@ -144,25 +144,10 @@ function createIsomorphLink(ctx) {
return new SchemaLink({ schema, context: context(ctx) })
} else {
const { HttpLink } = require('apollo-link-http')
const { setContext } = require('apollo-link-context')
const { ApolloLink } = require('apollo-link')
const cookie = require('js-cookie')

return ApolloLink.from([
setContext(() => {
const token = cookie.get('token')
if (token) {
return {
headers: {
authorization: `Bearer ${token}`,
},
}
}
}),
new HttpLink({
uri: '/api/graphql',
credentials: 'same-origin',
}),
])

return new HttpLink({
uri: '/api/graphql',
credentials: 'same-origin',
})
}
}
@@ -1,10 +1,6 @@
import models from '../models'
import jwt from 'jsonwebtoken'
import getConfig from 'next/config'

export const context = prevContext => ({
...prevContext,
...getConfig(),
models,
jwt,
})
Expand Up @@ -3,26 +3,19 @@ import {
ApolloError,
UserInputError,
} from 'apollo-server-micro'
import cookie from 'cookie'
import jwt from 'jsonwebtoken'
import getConfig from 'next/config'

const getToken = headers => {
if (headers.authorization) {
return headers.authorization.replace(/bearer\s/i, '')
} else if (headers.cookie.includes('token=')) {
let part = headers.cookie.slice(headers.cookie.indexOf('token=') + 6)
return part.slice(0, part.indexOf(';'))
}
}
const JWT_SECRET = getConfig().serverRuntimeConfig.JWT_SECRET

export const resolvers = {
Query: {
async viewer(_parent, _args, context, _info) {
const token = getToken(context.req.headers)
const { token } = cookie.parse(context.req.headers.cookie ?? '')
if (token) {
try {
const { id, email } = context.jwt.verify(
token,
context.serverRuntimeConfig.JWT_SECRET
)
const { id, email } = jwt.verify(token, JWT_SECRET)

return await context.models.User.findOne({ where: { id, email } })
} catch {
Expand Down Expand Up @@ -54,18 +47,43 @@ export const resolvers = {
})

if (user && user.validPassword(args.input.password)) {
const token = context.jwt.sign(
const token = jwt.sign(
{ email: user.email, id: user.id, time: new Date() },
context.serverRuntimeConfig.JWT_SECRET,
JWT_SECRET,
{
expiresIn: '6h',
}
)

return { user, token }
context.res.setHeader(
'Set-Cookie',
cookie.serialize('token', token, {
httpOnly: true,
maxAge: 6 * 60 * 60,
path: '/',
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
})
)

return { user }
}

throw new UserInputError('Invalid email and password combination')
},
async signOut(_parent, _args, context, _info) {
context.res.setHeader(
'Set-Cookie',
cookie.serialize('token', '', {
httpOnly: true,
maxAge: -1,
path: '/',
sameSite: 'lax',
secure: process.env.NODE_ENV === 'production',
})
)

return true
},
},
}
Expand Up @@ -22,7 +22,6 @@ export const typeDefs = gql`
type SignInPayload {
user: User!
token: String!
}
type Query {
Expand All @@ -34,5 +33,6 @@ export const typeDefs = gql`
type Mutation {
signUp(input: SignUpInput!): SignUpPayload!
signIn(input: SignInInput!): SignInPayload!
signOut: Boolean!
}
`
66 changes: 0 additions & 66 deletions examples/api-routes-apollo-server-and-client-auth/lib/auth.js

This file was deleted.

Expand Up @@ -12,18 +12,16 @@
"@apollo/react-ssr": "3.1.3",
"apollo-cache-inmemory": "1.6.5",
"apollo-client": "2.6.8",
"apollo-link-context": "1.0.19",
"apollo-link-http": "1.5.16",
"apollo-link-schema": "1.2.4",
"apollo-server-micro": "2.9.15",
"apollo-server-micro": "2.9.16",
"apollo-utilities": "^1.3.2",
"bcrypt": "3.0.7",
"cookie": "0.4.0",
"graphql": "^14.0.2",
"graphql-tag": "2.10.1",
"js-cookie": "2.2.1",
"jsonwebtoken": "8.5.1",
"next": "latest",
"next-cookies": "2.0.3",
"prop-types": "^15.6.2",
"react": "^16.7.0",
"react-dom": "^16.7.0",
Expand Down
20 changes: 16 additions & 4 deletions examples/api-routes-apollo-server-and-client-auth/pages/index.js
Expand Up @@ -2,7 +2,7 @@ import { withApollo } from '../apollo/client'
import gql from 'graphql-tag'
import Link from 'next/link'
import { useQuery } from '@apollo/react-hooks'
import { withAuthSync, signout } from '../lib/auth'
import { useRouter } from 'next/router'

const ViewerQuery = gql`
query ViewerQuery {
Expand All @@ -14,7 +14,16 @@ const ViewerQuery = gql`
`

const Index = () => {
const { data } = useQuery(ViewerQuery)
const router = useRouter()
const { data, loading } = useQuery(ViewerQuery)

if (
loading === false &&
data.viewer === null &&
typeof window !== 'undefined'
) {
router.push('/signin')
}

if (data && data.viewer) {
return (
Expand All @@ -23,12 +32,15 @@ const Index = () => {
<Link href="/about">
<a>static</a>
</Link>{' '}
page. or <button onClick={signout}>signout</button>
page. or{' '}
<Link href="/signout">
<a>signout</a>
</Link>
</div>
)
}

return <p>Loading...</p>
}

export default withAuthSync(withApollo(Index))
export default withApollo(Index)
Expand Up @@ -5,7 +5,7 @@ import gql from 'graphql-tag'
import { useMutation } from '@apollo/react-hooks'
import Field from '../components/field'
import { getStatusFrom } from '../lib/form'
import { signin } from '../lib/auth'
import { useRouter } from 'next/router'

const SignInMutation = gql`
mutation SignInMutation($email: String!, $password: String!) {
Expand All @@ -14,14 +14,14 @@ const SignInMutation = gql`
id
email
}
token
}
}
`

function SignIn() {
const [signIn] = useMutation(SignInMutation)
const [status, setStatus] = React.useState({})
const router = useRouter()
async function handleSubmit(event) {
event.preventDefault()
const emailElement = event.currentTarget.elements.email
Expand All @@ -34,8 +34,8 @@ function SignIn() {
password: passwordElement.value,
},
})
if (data.signIn.token) {
signin({ token: data.signIn.token })
if (data.signIn.user) {
router.push('/')
}
} catch (error) {
setStatus(getStatusFrom(error))
Expand Down
29 changes: 29 additions & 0 deletions examples/api-routes-apollo-server-and-client-auth/pages/signout.js
@@ -0,0 +1,29 @@
import React from 'react'
import { useMutation } from '@apollo/react-hooks'

import gql from 'graphql-tag'
import { useRouter } from 'next/router'
import { withApollo } from '../apollo/client'

const SignOutMutation = gql`
mutation SignOutMutation {
signOut
}
`

function SignOut() {
const router = useRouter()
const [signOut] = useMutation(SignOutMutation)

React.useEffect(() => {
if (typeof window !== 'undefined') {
signOut().then(() => {
router.push('/signin')
})
}
}, [signOut, router])

return <p>Signing out...</p>
}

export default withApollo(SignOut)

0 comments on commit 5dfc446

Please sign in to comment.