feat: Implement authentication with JWT
This commit is contained in:

committed by
Ömer Faruk Aydın

parent
f883dd1287
commit
c935f3f691
@@ -10,7 +10,6 @@ import appAssetsHandler from './helpers/app-assets-handler';
|
||||
import webUIHandler from './helpers/web-ui-handler';
|
||||
import errorHandler from './helpers/error-handler';
|
||||
import './config/database';
|
||||
import authentication from './helpers/authentication';
|
||||
|
||||
const app = express();
|
||||
const port = appConfig.port;
|
||||
@@ -21,7 +20,6 @@ app.use(morgan);
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(cors(corsOptions));
|
||||
app.use(authentication);
|
||||
app.use('/graphql', graphQLInstance);
|
||||
|
||||
webUIHandler(app);
|
||||
|
@@ -15,6 +15,7 @@ type AppConfig = {
|
||||
postgresEnableSsl: boolean;
|
||||
baseUrl?: string;
|
||||
encryptionKey: string;
|
||||
appSecretKey: string;
|
||||
serveWebAppSeparately: boolean;
|
||||
redisHost: string;
|
||||
redisPort: number;
|
||||
@@ -33,6 +34,7 @@ const appConfig: AppConfig = {
|
||||
postgresPassword: process.env.POSTGRES_PASSWORD,
|
||||
postgresEnableSsl: process.env.POSTGRES_ENABLE_SSL === 'true' ? true : false,
|
||||
encryptionKey: process.env.ENCRYPTION_KEY,
|
||||
appSecretKey: process.env.APP_SECRET_KEY,
|
||||
serveWebAppSeparately:
|
||||
process.env.SERVE_WEB_APP_SEPARATELY === 'true' ? true : false,
|
||||
redisHost: process.env.REDIS_HOST || '127.0.0.1',
|
||||
|
@@ -1,30 +1,35 @@
|
||||
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
||||
import User from '../../models/user';
|
||||
import userType from '../types/user';
|
||||
import authType from '../types/auth';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import appConfig from '../../config/app';
|
||||
|
||||
type Params = {
|
||||
email: string,
|
||||
password: string
|
||||
}
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
const loginResolver = async (params: Params) => {
|
||||
const user = await User.query().findOne({
|
||||
email: params.email,
|
||||
});
|
||||
|
||||
if (user && await user.login(params.password)) {
|
||||
return user;
|
||||
if (user && (await user.login(params.password))) {
|
||||
const token = jwt.sign({ userId: user.id }, appConfig.appSecretKey);
|
||||
|
||||
return { token, user };
|
||||
}
|
||||
|
||||
throw new Error('User could not be found.')
|
||||
}
|
||||
throw new Error('User could not be found.');
|
||||
};
|
||||
|
||||
const login = {
|
||||
type: userType,
|
||||
type: authType,
|
||||
args: {
|
||||
email: { type: GraphQLNonNull(GraphQLString) },
|
||||
password: { type: GraphQLNonNull(GraphQLString) }
|
||||
password: { type: GraphQLNonNull(GraphQLString) },
|
||||
},
|
||||
resolve: (_: any, params: any) => loginResolver(params)
|
||||
resolve: (_: any, params: any) => loginResolver(params),
|
||||
};
|
||||
|
||||
export default login;
|
||||
|
12
packages/backend/src/graphql/types/auth.ts
Normal file
12
packages/backend/src/graphql/types/auth.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { GraphQLObjectType, GraphQLString } from 'graphql';
|
||||
import UserType from './user';
|
||||
|
||||
const authType = new GraphQLObjectType({
|
||||
name: 'Auth',
|
||||
fields: {
|
||||
user: { type: UserType },
|
||||
token: { type: GraphQLString },
|
||||
},
|
||||
});
|
||||
|
||||
export default authType;
|
@@ -1,14 +1,33 @@
|
||||
import { Response, NextFunction } from 'express';
|
||||
import { rule, shield, allow } from 'graphql-shield';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import User from '../models/user';
|
||||
import RequestWithCurrentUser from '../types/express/request-with-current-user';
|
||||
import appConfig from '../config/app';
|
||||
|
||||
const authentication = async (req: RequestWithCurrentUser, _res: Response, next: NextFunction): Promise<void> => {
|
||||
// We set authentication to use the sample user we created temporarily.
|
||||
req.currentUser = await User.query().findOne({
|
||||
email: 'user@automatisch.com'
|
||||
}).throwIfNotFound();
|
||||
const isAuthenticated = rule()(async (_parent, _args, req) => {
|
||||
const token = req.headers['authorization'];
|
||||
|
||||
next()
|
||||
}
|
||||
if (token == null) return false;
|
||||
|
||||
try {
|
||||
const { userId } = jwt.verify(token, appConfig.appSecretKey) as {
|
||||
userId: string;
|
||||
};
|
||||
req.currentUser = await User.query().findById(userId).throwIfNotFound();
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const authentication = shield({
|
||||
Query: {
|
||||
'*': isAuthenticated,
|
||||
},
|
||||
Mutation: {
|
||||
'*': isAuthenticated,
|
||||
login: allow,
|
||||
},
|
||||
});
|
||||
|
||||
export default authentication;
|
||||
|
@@ -1,18 +1,20 @@
|
||||
import { graphqlHTTP } from 'express-graphql';
|
||||
import graphQLSchema from '../graphql/graphql-schema'
|
||||
import graphQLSchema from '../graphql/graphql-schema';
|
||||
import logger from '../helpers/logger';
|
||||
import { applyMiddleware } from 'graphql-middleware';
|
||||
import authentication from '../helpers/authentication';
|
||||
|
||||
const graphQLInstance = graphqlHTTP({
|
||||
schema: graphQLSchema,
|
||||
schema: applyMiddleware(graphQLSchema, authentication),
|
||||
graphiql: true,
|
||||
customFormatErrorFn: (error) => {
|
||||
logger.error(error.path + ' : ' + error.message + '\n' + error.stack)
|
||||
logger.error(error.path + ' : ' + error.message + '\n' + error.stack);
|
||||
|
||||
return {
|
||||
message: error.message,
|
||||
locations: error.locations
|
||||
}
|
||||
}
|
||||
})
|
||||
locations: error.locations,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default graphQLInstance;
|
||||
|
Reference in New Issue
Block a user