remove login gql resolver

This commit is contained in:
Hongbo Wu
2022-07-21 20:17:14 +08:00
committed by Jackson Harper
parent 86b950183c
commit abb1a414c1
7 changed files with 109 additions and 168 deletions

View File

@ -50,7 +50,6 @@ import {
googleLoginResolver,
googleSignupResolver,
labelsResolver,
loginResolver,
logOutResolver,
mergeHighlightResolver,
newsletterEmailsResolver,
@ -153,7 +152,6 @@ export const functionResolvers = {
createLabel: createLabelResolver,
updateLabel: updateLabelResolver,
deleteLabel: deleteLabelResolver,
login: loginResolver,
setLabels: setLabelsResolver,
generateApiKey: generateApiKeyResolver,
unsubscribe: unsubscribeResolver,
@ -570,7 +568,6 @@ export const functionResolvers = {
...resultResolveTypeResolver('Labels'),
...resultResolveTypeResolver('CreateLabel'),
...resultResolveTypeResolver('DeleteLabel'),
...resultResolveTypeResolver('Login'),
...resultResolveTypeResolver('SetLabels'),
...resultResolveTypeResolver('GenerateApiKey'),
...resultResolveTypeResolver('Search'),

View File

@ -10,7 +10,6 @@ import {
MutationDeleteAccountArgs,
MutationGoogleLoginArgs,
MutationGoogleSignupArgs,
MutationLoginArgs,
MutationUpdateUserArgs,
MutationUpdateUserProfileArgs,
QueryUserArgs,
@ -35,7 +34,6 @@ import { env } from '../../env'
import { validateUsername } from '../../utils/usernamePolicy'
import * as jwt from 'jsonwebtoken'
import { createUser } from '../../services/create_user'
import { comparePassword } from '../../utils/auth'
import { deletePagesByParam } from '../../elastic/pages'
import { setClaims } from '../../entity/utils'
import { User as UserEntity } from '../../entity/user'
@ -295,37 +293,6 @@ export function isErrorWithCode(error: unknown): error is ErrorWithCode {
)
}
export const loginResolver: ResolverFn<
LoginResult,
unknown,
WithDataSourcesContext,
MutationLoginArgs
> = async (_obj, { input }, { models, setAuth }) => {
const { email, password } = input
const user = await models.user.getWhere({
email,
})
if (!user?.id) {
return { errorCodes: [LoginErrorCode.UserNotFound] }
}
if (!user?.password) {
// user has no password, so they need to set one
return { errorCodes: [LoginErrorCode.WrongSource] }
}
// check if password is correct
const validPassword = await comparePassword(password, user.password)
if (!validPassword) {
return { errorCodes: [LoginErrorCode.InvalidCredentials] }
}
// set auth cookie in response header
await setAuth({ uid: user.id })
return { me: userDataToUser(user) }
}
export const deleteAccountResolver = authorized<
DeleteAccountSuccess,
DeleteAccountError,

View File

@ -37,9 +37,10 @@ import {
RegistrationType,
UserData,
} from '../../datalayer/user/model'
import { hashPassword } from '../../utils/auth'
import { comparePassword, hashPassword } from '../../utils/auth'
import { createUser } from '../../services/create_user'
import { isErrorWithCode } from '../../resolvers'
import { initModels } from '../../server'
const logger = buildLogger('app.dispatch')
const signToken = promisify(jwt.sign)
@ -359,56 +360,42 @@ export function authRouter() {
cors<express.Request>(corsConfig),
async (req: express.Request, res: express.Response) => {
const { email, password } = req.body
if (!email || !password) {
res.redirect(`${env.client.url}/email-login?errorCodes=AUTH_FAILED`)
return
}
const query = `
mutation login{
login(input: {
email: "${email}",
password: "${password}"
}) {
__typename
... on LoginError { errorCodes }
... on LoginSuccess {
me {
id
name
profile {
username
}
}
}
}
}`
try {
const result = await axios.post(env.server.gateway_url + '/graphql', {
query,
const models = initModels(kx, false)
const user = await models.user.getWhere({
email,
})
const { data } = result.data
if (data.login.__typename === 'LoginError') {
const errorCodes = data.login.errorCodes.join(',')
if (!user?.id) {
return res.redirect(
`${env.client.url}/email-login?errorCodes=${errorCodes}`
`${env.client.url}/email-login?errorCodes=${LoginErrorCode.UserNotFound}`
)
}
if (!result.headers['set-cookie']) {
if (!user?.password) {
// user has no password, so they need to set one
return res.redirect(
`${env.client.url}/${
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(req.params as any)?.action
}?errorCodes=unknown`
`${env.client.url}/email-login?errorCodes=${LoginErrorCode.WrongSource}`
)
}
res.setHeader('set-cookie', result.headers['set-cookie'])
// check if password is correct
const validPassword = await comparePassword(password, user.password)
if (!validPassword) {
return res.redirect(
`${env.client.url}/email-login?errorCodes=${LoginErrorCode.InvalidCredentials}`
)
}
await handleSuccessfulLogin(req, res, data.login.me, false)
// set auth cookie in response header
const token = await signToken({ uid: user.id }, env.server.jwtSecret)
res.cookie('auth', token, {
httpOnly: true,
expires: new Date(new Date().getTime() + 365 * 24 * 60 * 60 * 1000),
})
await handleSuccessfulLogin(req, res, user, false)
} catch (e) {
logger.info('email-login exception:', e)
res.redirect(`${env.client.url}/email-login?errorCodes=AUTH_FAILED`)

View File

@ -1410,11 +1410,6 @@ const schema = gql`
union UpdateLabelResult = UpdateLabelSuccess | UpdateLabelError
input LoginInput {
password: String!
email: String!
}
input SetLabelsInput {
pageId: ID!
labelIds: [ID!]!
@ -1825,7 +1820,6 @@ const schema = gql`
createLabel(input: CreateLabelInput!): CreateLabelResult!
updateLabel(input: UpdateLabelInput!): UpdateLabelResult!
deleteLabel(id: ID!): DeleteLabelResult!
login(input: LoginInput!): LoginResult!
setLabels(input: SetLabelsInput!): SetLabelsResult!
generateApiKey(input: GenerateApiKeyInput!): GenerateApiKeyResult!
unsubscribe(name: String!): UnsubscribeResult!

View File

@ -147,6 +147,6 @@ export const sendConfirmationEmail = async (user: User): Promise<boolean> => {
from: `Omnivore <${env.sender.message}>`,
to: user.email,
subject: 'Confirm your email',
text: `Hey ${user.name},\nPlease confirm your email by clicking the link below:\n\n${confirmationLink}\n\n`,
text: `Hey ${user.name},\n\nPlease confirm your email by clicking the link below:\n\n${confirmationLink}\n\n`,
})
}

View File

@ -2,7 +2,6 @@ import { createTestUser, deleteTestUser, getProfile, getUser } from '../db'
import { graphqlRequest, request } from '../util'
import { expect } from 'chai'
import {
LoginErrorCode,
UpdateUserErrorCode,
UpdateUserProfileErrorCode,
} from '../../src/generated/graphql'
@ -238,89 +237,4 @@ describe('User API', () => {
return graphqlRequest(query, invalidAuthToken).expect(500)
})
})
describe('login', () => {
let query: string
let email: string
let password: string
beforeEach(() => {
query = `
mutation {
login(
input: {
email: "${email}"
password: "${password}"
}
) {
... on LoginSuccess {
me {
id
name
profile {
username
}
}
}
... on LoginError {
errorCodes
}
}
}
`
})
context('when email and password are valid', () => {
before(() => {
email = user.email
password = correctPassword
})
it('responds with 200', async () => {
const res = await graphqlRequest(query).expect(200)
expect(res.body.data.login.me.id).to.eql(user.id)
})
})
context('when user not exists', () => {
before(() => {
email = 'Some email'
})
it('responds with error code UserNotFound', async () => {
const response = await graphqlRequest(query).expect(200)
expect(response.body.data.login.errorCodes).to.eql([
LoginErrorCode.UserNotFound,
])
})
})
context('when user has no password stored in db', () => {
before(() => {
email = anotherUser.email
password = 'Some password'
})
it('responds with error code WrongSource', async () => {
const response = await graphqlRequest(query).expect(200)
expect(response.body.data.login.errorCodes).to.eql([
LoginErrorCode.WrongSource,
])
})
})
context('when password is wrong', () => {
before(() => {
email = user.email
password = 'Some password'
})
it('responds with error code UserNotFound', async () => {
const response = await graphqlRequest(query).expect(200)
expect(response.body.data.login.errorCodes).to.eql([
LoginErrorCode.InvalidCredentials,
])
})
})
})
})

View File

@ -8,6 +8,7 @@ import { MailDataRequired } from '@sendgrid/helpers/classes/mail'
import sinon from 'sinon'
import * as util from '../../src/utils/sendEmail'
import supertest from 'supertest'
import { hashPassword } from '../../src/utils/auth'
describe('auth router', () => {
const route = '/api/auth'
@ -139,4 +140,85 @@ describe('auth router', () => {
})
})
})
describe('login', () => {
const loginRequest = (email: string, password: string): supertest.Test => {
return request.post(`${route}/email-login`).send({
email,
password,
})
}
const correctPassword = 'correctPassword'
let user: User
let email: string
let password: string
before(async () => {
const hashedPassword = await hashPassword(correctPassword)
user = await createTestUser('login_test_user', undefined, hashedPassword)
})
after(async () => {
await deleteTestUser(user.name)
})
context('when email and password are valid', () => {
before(() => {
email = user.email
password = correctPassword
})
it('redirects to waitlist page', async () => {
const res = await loginRequest(email, password).expect(302)
expect(res.header.location).to.endWith('/waitlist')
})
})
context('when user not exists', () => {
before(() => {
email = 'Some email'
})
it('redirects with error code UserNotFound', async () => {
const res = await loginRequest(email, password).expect(302)
expect(res.header.location).to.endWith(
'/email-login?errorCodes=USER_NOT_FOUND'
)
})
})
context('when user has no password stored in db', () => {
before(async () => {
const anotherUser = await createTestUser('another_user')
email = anotherUser.email
password = 'Some password'
})
after(async () => {
await deleteTestUser('another_user')
})
it('redirects with error code WrongSource', async () => {
const res = await loginRequest(email, password).expect(302)
expect(res.header.location).to.endWith(
'/email-login?errorCodes=WRONG_SOURCE'
)
})
})
context('when password is wrong', () => {
before(() => {
email = user.email
password = 'Wrong password'
})
it('redirects with error code InvalidCredentials', async () => {
const res = await loginRequest(email, password).expect(302)
expect(res.header.location).to.endWith(
'/email-login?errorCodes=INVALID_CREDENTIALS'
)
})
})
})
})