Fix tests

This commit is contained in:
Hongbo Wu 2022-05-27 12:38:06 +08:00
parent 9051a3f43a
commit 333b0259ba
7 changed files with 50 additions and 43 deletions

View file

@ -49,10 +49,16 @@ const contextFunc: ContextFunction<ExpressContext, ResolverContext> = async ({
})
if (token) {
jwt.verify(token, env.server.jwtSecret) &&
(claims = jwt.decode(token) as Claims)
if (!claims) {
claims = await claimsFromApiKey(token)
try {
jwt.verify(token, env.server.jwtSecret) &&
(claims = jwt.decode(token) as Claims)
} catch (e) {
if (e instanceof jwt.JsonWebTokenError) {
logger.info(`not a jwt token, checking api key`, { token })
claims = await claimsFromApiKey(token)
} else {
throw e
}
}
}

View file

@ -10,7 +10,7 @@ import { authorized } from '../../utils/helpers'
import { getRepository } from '../../entity/utils'
import { User } from '../../entity/user'
import { ApiKey } from '../../entity/api_key'
import { generateApiKey, hashKey } from '../../utils/auth'
import { generateApiKey, hashApiKey } from '../../utils/auth'
export const generateApiKeyResolver = authorized<
GenerateApiKeySuccess,
@ -41,7 +41,7 @@ export const generateApiKeyResolver = authorized<
await getRepository(ApiKey).save({
user: { id: uid },
name,
key: hashKey(apiKey),
key: hashApiKey(apiKey),
expiresAt: exp,
})

View file

@ -33,7 +33,7 @@ import { env } from '../../env'
import { validateUsername } from '../../utils/usernamePolicy'
import * as jwt from 'jsonwebtoken'
import { createUser } from '../../services/create_user'
import { compareHashedKey, hashKey } from '../../utils/auth'
import { comparePassword, hashPassword } from '../../utils/auth'
export const updateUserResolver = authorized<
UpdateUserSuccess,
@ -310,7 +310,7 @@ export const loginResolver: ResolverFn<
}
// check if password is correct
const validPassword = compareHashedKey(password, user.password)
const validPassword = await comparePassword(password, user.password)
if (!validPassword) {
return { errorCodes: [LoginErrorCode.InvalidCredentials] }
}
@ -331,7 +331,7 @@ export const signupResolver: ResolverFn<
try {
// hash password
const hashedPassword = hashKey(password)
const hashedPassword = await hashPassword(password)
const [user, profile] = await createUser({
email,

View file

@ -3,13 +3,14 @@ import { v4 as uuidv4 } from 'uuid'
import { Claims } from '../resolvers/types'
import { getRepository } from '../entity/utils'
import { ApiKey } from '../entity/api_key'
import crypto from 'crypto'
export const hashKey = (key: string, salt = 10) => {
return bcrypt.hashSync(key, salt)
export const hashPassword = async (password: string, salt = 10) => {
return bcrypt.hash(password, salt)
}
export const compareHashedKey = (rawKey: string, hash: string) => {
return bcrypt.compareSync(rawKey, hash)
export const comparePassword = async (password: string, hash: string) => {
return bcrypt.compare(password, hash)
}
export const generateApiKey = (): string => {
@ -17,17 +18,26 @@ export const generateApiKey = (): string => {
return uuidv4()
}
export const claimsFromApiKey = async (
key: string
): Promise<Claims | undefined> => {
const hashedKey = hashKey(key)
export const hashApiKey = (apiKey: string) => {
return crypto.createHash('sha256').update(apiKey).digest('hex')
}
export const claimsFromApiKey = async (key: string): Promise<Claims> => {
const hashedKey = hashApiKey(key)
const apiKey = await getRepository(ApiKey).findOne({
where: { key: hashedKey },
where: {
key: hashedKey,
},
relations: ['user'],
})
if (!apiKey) {
console.error('api key not found')
return undefined
throw new Error('api key not found')
}
const iat = Math.floor(Date.now() / 1000)
const exp = Math.floor(new Date(apiKey.expiresAt).getTime() / 1000)
if (exp < iat) {
throw new Error('api key expired')
}
// update last used
@ -35,7 +45,7 @@ export const claimsFromApiKey = async (
return {
uid: apiKey.user.id,
iat: new Date().getTime(),
exp: apiKey.expiresAt.getTime(),
iat,
exp,
}
}

View file

@ -1,7 +1,7 @@
import { createTestUser, deleteTestUser } from '../db'
import { graphqlRequest, request } from '../util'
import { User } from '../../src/entity/user'
import { hashKey } from '../../src/utils/auth'
import { hashPassword } from '../../src/utils/auth'
import 'mocha'
describe('Sanitize Directive', () => {
@ -12,7 +12,7 @@ describe('Sanitize Directive', () => {
let user: User
before(async () => {
const hashedPassword = hashKey(correctPassword)
const hashedPassword = await hashPassword(correctPassword)
user = await createTestUser(username, '', hashedPassword)
const res = await request
.post('/local/debug/fake-user-login')

View file

@ -28,7 +28,8 @@ describe('generate api key', () => {
let authToken: string
let user: User
let query: string
let expiredAt: string
let expiresAt: string
let name: string
before(async () => {
// create test user and login
@ -49,7 +50,8 @@ describe('generate api key', () => {
query = `
mutation {
generateApiKey(input: {
expiredAt: "${expiredAt}"
name: "${name}"
expiresAt: "${expiresAt}"
}) {
... on GenerateApiKeySuccess {
apiKey
@ -62,22 +64,10 @@ describe('generate api key', () => {
`
})
context('when no expiredAt is specified', () => {
before(() => {
expiredAt = ''
})
it('should generate an api key with no expiration date', async () => {
const response = await graphqlRequest(query, authToken)
expect(response.body.data.generateApiKey.apiKey).to.be.a('string')
return testAPIKey(response.body.data.generateApiKey.apiKey).expect(200)
})
})
context('when api key is not expired', () => {
before(() => {
expiredAt = new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString()
name = 'test'
expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString()
})
it('should generate an api key', async () => {
@ -90,7 +80,8 @@ describe('generate api key', () => {
context('when api key is expired', () => {
before(() => {
expiredAt = new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString()
name = 'test-expired'
expiresAt = new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString()
})
it('should generate an expired api key', async () => {

View file

@ -8,7 +8,7 @@ import {
UpdateUserProfileErrorCode,
} from '../../src/generated/graphql'
import { User } from '../../src/entity/user'
import { hashKey } from '../../src/utils/auth'
import { hashPassword } from '../../src/utils/auth'
import 'mocha'
describe('User API', () => {
@ -21,7 +21,7 @@ describe('User API', () => {
let anotherUser: User
before(async () => {
const hashedPassword = hashKey(correctPassword)
const hashedPassword = await hashPassword(correctPassword)
// create test user and login
user = await createTestUser(username, '', hashedPassword)
const res = await request