Update generateApiKey resolver

This commit is contained in:
Hongbo Wu 2022-05-27 11:22:50 +08:00
parent 849adf84d4
commit a09588d3a7
9 changed files with 67 additions and 37 deletions

View file

@ -570,12 +570,15 @@ export type GenerateApiKeyError = {
};
export enum GenerateApiKeyErrorCode {
BadRequest = 'BAD_REQUEST'
AlreadyExists = 'ALREADY_EXISTS',
BadRequest = 'BAD_REQUEST',
Unauthorized = 'UNAUTHORIZED'
}
export type GenerateApiKeyInput = {
expiredAt?: InputMaybe<Scalars['Date']>;
scope?: InputMaybe<Scalars['String']>;
expiresAt: Scalars['Date'];
name: Scalars['String'];
scopes?: InputMaybe<Array<Scalars['String']>>;
};
export type GenerateApiKeyResult = GenerateApiKeyError | GenerateApiKeySuccess;

View file

@ -500,12 +500,15 @@ type GenerateApiKeyError {
}
enum GenerateApiKeyErrorCode {
ALREADY_EXISTS
BAD_REQUEST
UNAUTHORIZED
}
input GenerateApiKeyInput {
expiredAt: Date
scope: String
expiresAt: Date!
name: String!
scopes: [String!]
}
union GenerateApiKeyResult = GenerateApiKeyError | GenerateApiKeySuccess

View file

@ -4,33 +4,53 @@ import {
GenerateApiKeySuccess,
MutationGenerateApiKeyArgs,
} from '../../generated/graphql'
import { generateApiKey } from '../../utils/auth'
import { analytics } from '../../utils/analytics'
import { env } from '../../env'
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'
export const generateApiKeyResolver = authorized<
GenerateApiKeySuccess,
GenerateApiKeyError,
MutationGenerateApiKeyArgs
>((_, { input: { scope, expiredAt } }, { claims }) => {
>(async (_, { input: { name, expiresAt } }, { claims: { uid }, log }) => {
try {
console.log('generateApiKeyResolver', scope, expiredAt)
log.info('generateApiKeyResolver')
const user = await getRepository(User).findOneBy({ id: uid })
if (!user) {
return {
errorCodes: [GenerateApiKeyErrorCode.Unauthorized],
}
}
const exp = expiredAt ? new Date(expiredAt).getTime() / 1000 : null
const apiKey = generateApiKey({
iat: new Date().getTime(),
scope: scope || 'all',
uid: claims.uid,
...(exp && { exp }),
const existingApiKey = await getRepository(ApiKey).findOneBy({
user: { id: uid },
name,
})
if (existingApiKey) {
return {
errorCodes: [GenerateApiKeyErrorCode.AlreadyExists],
}
}
const exp = new Date(expiresAt)
const apiKey = generateApiKey()
await getRepository(ApiKey).save({
user: { id: uid },
name,
key: hashKey(apiKey),
expiresAt: exp,
})
analytics.track({
userId: claims.uid,
event: 'generate_api_key',
userId: uid,
event: 'api_key_generated',
properties: {
scope,
expiredAt: exp,
name,
expiresAt: exp,
env: env.server.apiEnv,
},
})
@ -38,6 +58,7 @@ export const generateApiKeyResolver = authorized<
return { apiKey }
} catch (error) {
console.error(error)
return { errorCodes: [GenerateApiKeyErrorCode.BadRequest] }
}
})

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 { comparePassword, hashPassword } from '../../utils/auth'
import { compareHashedKey, hashKey } from '../../utils/auth'
export const updateUserResolver = authorized<
UpdateUserSuccess,
@ -310,7 +310,7 @@ export const loginResolver: ResolverFn<
}
// check if password is correct
const validPassword = comparePassword(password, user.password)
const validPassword = compareHashedKey(password, user.password)
if (!validPassword) {
return { errorCodes: [LoginErrorCode.InvalidCredentials] }
}
@ -331,7 +331,7 @@ export const signupResolver: ResolverFn<
try {
// hash password
const hashedPassword = hashPassword(password)
const hashedPassword = hashKey(password)
const [user, profile] = await createUser({
email,

View file

@ -1412,8 +1412,9 @@ const schema = gql`
}
input GenerateApiKeyInput {
scope: String
expiredAt: Date
name: String!
scopes: [String!]
expiresAt: Date!
}
union GenerateApiKeyResult = GenerateApiKeySuccess | GenerateApiKeyError
@ -1428,6 +1429,8 @@ const schema = gql`
enum GenerateApiKeyErrorCode {
BAD_REQUEST
ALREADY_EXISTS
UNAUTHORIZED
}
# Query: search

View file

@ -1,16 +1,15 @@
import * as bcrypt from 'bcryptjs'
import * as jwt from 'jsonwebtoken'
import { env } from '../env'
import { Claims } from '../resolvers/types'
import { v4 as uuidv4 } from 'uuid'
export const hashPassword = (password: string) => {
return bcrypt.hashSync(password, 10)
export const hashKey = (key: string, salt = 10) => {
return bcrypt.hashSync(key, salt)
}
export const comparePassword = (password: string, hash: string) => {
return bcrypt.compareSync(password, hash)
export const compareHashedKey = (rawKey: string, hash: string) => {
return bcrypt.compareSync(rawKey, hash)
}
export const generateApiKey = (claims: Claims): string => {
return jwt.sign(claims, env.server.jwtSecret)
export const generateApiKey = (): string => {
// TODO: generate random string key
return uuidv4()
}

View file

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

View file

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

View file

@ -12,7 +12,8 @@ CREATE TABLE omnivore.api_key (
scopes text[] NOT NULL DEFAULT '{}',
expires_at timestamptz NOT NULL,
created_at timestamptz NOT NULL DEFAULT current_timestamp,
used_at timestamptz
used_at timestamptz,
UNIQUE (user_id, name)
);
COMMIT;