feat: Implement authentication with JWT

This commit is contained in:
Faruk AYDIN
2022-03-05 19:58:52 +03:00
committed by Ömer Faruk Aydın
parent f883dd1287
commit c935f3f691
9 changed files with 199 additions and 31 deletions

View File

@@ -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);

View File

@@ -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',

View File

@@ -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;

View 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;

View File

@@ -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;

View File

@@ -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;