1.0.2 β€’ Published 5 years ago

wezt-authentication v1.0.2

Weekly downloads
-
License
MIT
Repository
-
Last release
5 years ago

WEZT-AUTHENTICATION

Simplify user authentication on your express app! πŸ‘©β€πŸ’ΌπŸ‘¨β€πŸ’Ό OBS: This package is not recomended if you need a high-level of customization in your app's authentication system. We'll keep working on customization but it is not our focus on development. Our mission is to deliver a reliable, safe and simple way to register and manage your users.

Installation

$ npm i wezt-authentication

Importing

const Authentication = require('wezt-authentication');

// ES6
import Authentication,  {
	AuthenticationError,
	/**
	 * wezt.AuthenticationError, extends Error
	 * Error class that receives a status code on constructor. If you're willing to extend your authentication logic, you should create your own error classes extending this AuthenticationError. 
	 * Usage #1: throw new AuthenticationError('Unknown parameter', 400);
	 * Usage #2: class BadRequestError extends AuthenticationError {
	 *  	constructor(message?: string) {
	 *  		super(message || "Unknown parameter", 400);
	 * 			{...}
	 * 		}
	 * }
	 * throw new BadRequestError();
	 *  */ 
	Controller,
	/**
	 * wezt.Controller
	 * When creating other controllers for your applications, extend/implement this interface.
	 * Usage #1: const PostController: Controller = {
	 * 		store: (req: Request, res: Response) => { ... },
	 * 		...
	 * }
	 * Usage #2: interface IPostController extends Controller { ... }
	 * Usage #3: class PostController implements Controller { 
	 * 		store(req: Request, res: Response) { 
	 * 			... 
	 * 		};
	 * 		...
	 * }
	 *  */ 
} from 'wezt-authentication';

Documentation

Parameters

References to used packages: express mongoose express-session nodemailer

Authentication({
	app,
	/**
	 *	express.Application
	 *	Reference to an express application.
	 *	Ex: express()
	 *	*/
	connection,
	/**
	 *	mongoose.Connection
	 *	Reference to a mongoose connection.
	 *	Ex: mongoose.createConnection(uri, options)
	 *	*/
	nodemailer: { 
	//	undefined | object
	//	Only required when routes is set as an object or as true.
		smtp, 
		/**
		 *	nodemailer.SMTPTransport.Options
		 *	SMTP Transporter configuration for sending mails.
		 *	Ex: {
		 *		service: 'gmail',
		 *		auth: {
		 *			user: 'yourEmail@gmail.com',
		 *			pass: 'yourPassword',
		 *		}
		 *	}
		 *	*/,
		mail, 
		/**
		 *	nodemailer.SendMailOptions
		 *	Options object to customize your password recovery mail.
		 *	There is some placeholders you can insert on "text" property to retrieve some useful information.
		 *	Currently supported placeholders:
		 *	{{code}} legible 6-digits code
		 *	{{token}} one-time use jsonwebtoken to recover password
		 *	Ex: {
		 *		from: 'yourEmail@gmail.com',
		 *		subject: 'Password recovery',
		 *		text: 'There is your recovery code: {{code}}. Click on this link to redirect to the password recovery page: https://yourdomain.com/recover/{{token}}/{{code}}',
		 *	}
		 *	*/
	}, 
	sessionOptions,
	/**
	 *	session.SessionOptions
	 *	Options object to create a session middleware using express-session
	 *	Ex: {
	 *		secret: "yourSecretKey",
	 *		cookie: {
	 *			maxAge: 30 * 24 * 60 * 60 * 1000    
	 *		},
	 *		name: "SESSID",
	 *		resave: false,
	 *		saveUninitialized: false, 
	 *	}
	 *	*/
	roles,
	/**
	 *	string[]
	 *	Array of strings containing all possible roles for users.
	 *	Ex: ["Admin", "Manager", "Client"]
	 *	*/ 
	administrativeRoles,
	/**
	 *	string[]
	 *	Array of strings containing all administrative roles, that have full access to user management.
	 *	Ex: ["Admin", "Client"]
	 *	*/ 
	defaultRole,
	/**
	 *	string
	 *	String that specifies the default role assigned to a 
	 *	created or registered user.
	 *	Ex: "Client"
	 *	*/
	routes,
	/**
	 *	undefined | boolean | object 
	 *	Whether you want or not to automatically set routes for your application.
	 *	Setting this as false will require you to set your routes and paths manually.
	 *	Defaults to false.
	 *	Ex: false | {
	 *		authPrefix: '/', // default: /auth
	 *		profilePrefix: '/me', // default: /profile
	 *		usersPrefix: '/clients'
	 *	}
	 *	*/
	userSchema,
	/**
	 *	undefined | mongoose.Schema
	 *	Optional property to set your custom mongoose user schema. By modifying the user schema, you may need to overwrite the authentication logic with your own code.
	 *	Ex: new mongoose.Schema({
	 *		name: {
	 *		  type: String,
	 *		  required: true,
	 *		},
	 *		username: {
	 *		  type: String,
	 *		  required: true,
	 *		  unique: true,
	 *		},
	 *		password: {
	 * 			type: String,
	 *			required: true,
	 *		},
	 *	}, { timestamps: true })
	 *	*/ 
})

Return

const {
	authorize,
	/**
	 * express.RequestHandler
	 * Middleware to authenticate and authorize users based on role.
	 * "roles" parameter is optional. When undefined, it only authenticates the user.
	 * Usage #1: routes.post('/post/:postId/comments, authorize(), CommentController.store);
	 * Usage #2: routes.post('/products, authorize(['Admin', 'Manager']), ProductsController.store); 
	 * */
	mustNotBeAuthenticated,
	/**
	 * express.RequestHandler
	 * Middleware to ensure the user accessing the route is not authenticated. Useful to prevent session overwrites and other exploitations. 
	 * Usage: routes.post('/login', mustNotBeAuthenticated(), MyOwnAuthController.login);
	 * */
	errorHandler,
	/**
	 * express.ErrorRequestHandler
	 * A basic Error handler to catch and log errors. Be sure to use this middleware after using all your routes!
	 * routes.use('/posts', postsRoutes);
	 * {...}
	 * 
	 * app.use('/api', routes);
	 * app.use(errorHandler);
	 * 
	 * app.listen(...);
	 * */
  UserController,
	/**
	 * wezt.Controller
	 * Controller containing administrative CRUD for the User Model. Use this when setting "routes" as false, and when willing to overwrite controller functions with your own code. Be sure to not allow common users to have access to this routes, as they have full controll over all users on database!
	 * Usage: const { UserController, routes, authorize } = Authentication({...config, routes: false });
	 * UserController.index = (req: express.Request, res: express.Response) => {
	 * 		...yourOwnLogic
	 * }
	 * routes.post("/Your/Own/Path", [...YourMiddlewares, authorize('Admin')], UserController.store);
	 * routes.post("/Your/Own/Path", [...YourMiddlewares, authorize('Admin')], UserController.index);
	 * */
  AuthController,
	/**
	 * wezt.IAuthController, extends wezt.Controller
	 * Controller containing profile CRUD and authentication logic, such as login, logout, register and password recovery.
	 * Usage: Same as above
	 * */
  User,
	/**
	 * wezt.UserModel<UserDocument>, extends mongoose.Model
	 * Model that manipulates the "users" collection on mongodb. Only useful when extending the authentication logic.
	 * Usage: await User.create({...});
	 * */
  transporter: Mail,
	/**
	 * nodemailer.Mail
	 * Use this transporter if your app will need to send mails in other routes.
	 * Usage: See nodemailer docs
	 * */
  routes: Router,
	/**
	 * express.Router
	 * When config property "routes" is set as true, use this routes to include the rest of your application.
	 * Usage: const { routes } = Authentication(config);
	 * 
	 * routes.use('/posts', PostController);
	 * routes.use('/products', ProductController);
	 * 
	 * app.use('/api', routes);
	 * */
} = Authentication(config);

Basic usage

import Authentication from 'wezt';
import express from 'express';
import mongoose from 'mongoose';

const app = express();
const connection = mongoose.createConnection('mongodb://localhost:27017/wezt-test', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useCreateIndex: true,
});

const { errorHandler, routes } = Authentication({
  app,
  connection,
  routes: true,
	roles: ['Admin', 'User'],
	administrativeRoles: ['Admin'],
  defaultRole: 'User',
  nodemailer: {
    smtp: {
      service: 'gmail',
      auth: {
        user: '<youremail@gmail.com>',
        pass: '<yourpassword>',
      },

    },
    mail: {
      from: '<youremail@gmail.com>',
      subject: 'Password recovery',
      text: 'There is your recovery code: {{code}}. Click on this link to redirect to the password recovery page: http://yourdomain.com/{{token}}/{{code}}.',
    },
  },
  sessionOptions: {
    secret: '<secret>',
    cookie: {
      maxAge: 30 * 24 * 60 * 60 * 1000,
    },
    name: '<session cookie name>',
    resave: false,
    saveUninitialized: false,
  },
});

app.use('/api', routes);
app.use(errorHandler);

const port = process.env.PORT || 3333;
app.listen(port, (err) => {
	if(err) throw err;
	console.log(`Connected on port ${port}`);
});

Generated Endpoints

Every POST/PUT route does NOT accept unknown properties on body object.

POST /auth/login

Authorization: everyone

Body { email: string; password: string; }

Return object 200 - { user: UserDocument; } 400, 401, 404, 500 0- { error: string; }

Result Generates a session and returns a cookie via Headers.

POST /auth/logout

Authorization: authenticated users 204 - No Content; 401 - { error: string; }

Result Destroys an User Session.

POST /auth/register

Authorization: not authenticated users

Body UserObject { email: string; username: string; password: string; }

Return object 201 - { user: UserDocument; } 400, 409, 500 - { error: string; }

Result Generates a new User and return it as an object, generates a session and return a cookie via Headers.

POST /auth/recover

Authorization: not authenticated users

Body { email: string; }

Return object 200 - { token: string; (JsonWebToken) } 400, 404, 500 - { error: string; }

Result Generates a JsonWebToken signed with a 6-Digit code that is sent to the user's email.

GET /auth/validateToken/:token/:code

Authorization: not authenticated users

Parameters { token: string; (JsonWebToken) code: string; (6-Digit code) }

Return object 204 - No Content; 400, 401, 500 - { error: string; }

Result Returns a 204 status code with no content if the given code successfully decrypts the given token.

POST /auth/reset/:token/:code

Authorization: not authenticated users

Parameters { token: string; (JsonWebToken) code: string; (6-Digit code) }

Body { password: string; }

Return object 200 - { user: UserDocument; } 400, 401, 500 - { error: string; }

Result Modifies user's password, generates a session and return a cookie via Headers.

GET /users

Authorization: administrativeRoles

Query { username?: string; email?: string; limit?: string; default = 20 page?: string; default = 1 sortBy?: string; sort?: 'asc' | 'desc'; default = 'asc' }

Return object 200 - { docs: UserDocument[]; totalDocs: number; limit: number; totalPages: number; page: number; pagingCounter: number; hasPrevPage: boolean; hasNextPage: boolean; prevPage: number | null; nextPage: number | null; } 400, 401, 403 - { error: string; }

Result Returns an array containing several users that satisfies the given query.

GET /users/:id

Authorization: administrativeRoles

Parameters { id: mongoose.Types.ObjectId; }

Return object 200 - { user: UserDocument; } 400, 401, 403, 404 - { error: string; }

Result Returns the User document that matches the given Id.

POST /users

Authorization: administrativeRoles

Body { username: string; email: string; password: string; role?: string; }

Return object 200 - { user: UserDocument; } 400, 401, 403 - { error: string; }

Result Creates a new user.

PUT /users/:id

Authorization: administrativeRoles

Parameters { id: mongoose.Types.ObjectId; }

Body { username?: string; email?: string; password?: string; role?: string; }

Return object 200 - { user: UserDocument; } 400, 401, 403, 404 - { error: string; }

DELETE /users/:id

Authorization: administrativeRoles

Parameters { id: mongoose.Types.ObjectId; }

Return object 204 - No content 400, 401, 403, 404 - { error: string; }

GET /profile

Authorization: authenticated user

Return object 200 - { user: UserDocument; } 401, 404, 500 - { error: string; }

PUT /profile

Authorization: authenticated user

Body { username?: string; email?: string; password?: string; role?: string; }

Return object 200 - { user: UserDocument; } 400, 401, 500 - { error: string; }

DELETE /profile

Authorization: authenticated user

Return object 204 - No Content 401, 404 - { error: string; }