Skip to content

Instantly share code, notes, and snippets.

@Fabianopb
Last active September 16, 2018 12:42
Show Gist options
  • Save Fabianopb/e6939727ad4735bb3027653b68037c99 to your computer and use it in GitHub Desktop.
Save Fabianopb/e6939727ad4735bb3027653b68037c99 to your computer and use it in GitHub Desktop.
Authentication for express-react-ts-ci Medium post
import * as jwt from "express-jwt";
// A-ha! So this is where the AUTH_SHARED_SECRET from .env is used!
export const authorize = jwt({
secret: process.env.AUTH_SHARED_SECRET
});
import * as crypto from "crypto";
import * as jwt from "jsonwebtoken";
import * as mongoose from "mongoose";
// Declare the model interface
interface User extends mongoose.Document {
email: string;
hash: string;
salt: string;
setPassword(password: string): void;
isPasswordValid(password: string): boolean;
generateJwt(): { token: string; expiry: Date };
}
// Declare the model schema
const userSchema = new mongoose.Schema({
email: {
type: String,
// Important! We want users to be unique
unique: true,
required: true
},
hash: {
type: String,
required: true
},
salt: {
type: String,
required: true
}
});
// Define some public methods for our model
class UserClass {
private _id: string;
private email: string;
private salt: string;
private hash: string;
// Create a salt and hash from the password
public setPassword(password: string) {
this.salt = crypto.randomBytes(16).toString("hex");
this.hash = crypto.pbkdf2Sync(password, this.salt, 100000, 512, "sha512").toString("hex");
}
// Check if hashes match
public isPasswordValid(password: string): boolean {
const hash = crypto.pbkdf2Sync(password, this.salt, 100000, 512, "sha512").toString("hex");
return this.hash === hash;
}
// Generate access token for 30 minutes
public generateJwt(): { token: string; expiry: Date } {
const expiry = new Date();
expiry.setMinutes(expiry.getMinutes() + 30);
const token = jwt.sign({
_id: this._id,
email: this.email,
exp: Math.round(expiry.getTime() / 1000),
}, process.env.AUTH_SHARED_SECRET);
return { token, expiry };
}
}
// Important! Don't forget to use loadClass so your new methods will be included in the model
userSchema.loadClass(UserClass);
export default mongoose.model<User>("User", userSchema);
import * as bodyParser from "body-parser";
import { Router } from "express";
import * as mongoose from "mongoose";
import * as passport from "passport";
import { Strategy } from "passport-local";
import { authorize } from "../config";
import User from "./user.model";
passport.use(new Strategy({ usernameField: "email" }, async (username, password, done) => {
try {
// Tries to find the user matching the given username
const user = await User.findOne({ email: username });
// Check if the password is valid
if (user && user.isPasswordValid(password)) {
return done(null, user);
} else {
// Throws an error if credentials are not valid
throw new Error("Invalid credentials");
}
} catch (error) {
return done(error);
}
}));
const router = Router();
router.route("/register").post(bodyParser.json(), async (request, response) => {
try {
const user = new User();
user.email = request.body.email;
// Set the password hash with the method created in the model
user.setPassword(request.body.password);
await user.save();
// Returns a new token upon registering a user
const tokenSignature = user.generateJwt();
return response.status(200).json(tokenSignature);
} catch (error) {
return response.status(400).send(error);
}
});
router.route("/login").post(bodyParser.json(), (request, response) => {
// Use passport to authenticate user login
passport.authenticate("local", (error, user) => {
if (!user) {
return response.status(400).json({ error: error.message });
}
// If login is valid generate a token and return it to the user
const tokenSignature = user.generateJwt();
return response.status(200).json(tokenSignature);
})(request, response);
});
// This is an example of a protected route. Notice that we call `authorize` in the first place!
router.route("/profile").get(authorize, async (request, response) => {
const user = await User.findById(request.user._id);
return response.status(200).json(user);
});
export default router;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment