diff --git a/packages/component-admin/.gitignore b/packages/component-admin/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3614a810088d89d9ccaa28d82401545634874a18
--- /dev/null
+++ b/packages/component-admin/.gitignore
@@ -0,0 +1,8 @@
+_build/
+api/
+logs/
+node_modules/
+uploads/
+.env.*
+.env
+config/local*.*
\ No newline at end of file
diff --git a/packages/component-admin/README.md b/packages/component-admin/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..6107c057207697467a41159bcc3f0eee270ae181
--- /dev/null
+++ b/packages/component-admin/README.md
@@ -0,0 +1,42 @@
+# AWS SES
+
+In order to use `component-aws-ses` you first need to have a `.env` file containing AWS data in the root folder of the starting point of your application.
+
+The `.env` file contain the following data:
+```bash
+AWS_SES_SECRET_KEY = <secretKey>
+AWS_SES_ACCESS_KEY = <accessKey>
+EMAIL_SENDER = verified_ses_sender@domain.com
+AWS_SES_REGION = region-name
+```
+
+Then, as soon as possible in your app you should add the `dotenv` package:
+```js
+require('dotenv').config()
+```
+
+# `component-aws-ses` API
+A list of endpoints that help you upload, download and delete S3 files.
+
+## Send an email [POST]
+#### Request
+`POST /api/email`
+#### Request body
+
+All parameters are `required`
+```json
+{
+	"email": "to_email@domain.com",
+	"subject": "Example subject",
+	"textBody": "this is an email",
+	"htmlBody": "<p><b>This</b> is an <i>email</i>"
+}
+```
+#### Response
+```json
+HTTP/1.1 204
+```
+
+
+
+
diff --git a/packages/component-admin/index.js b/packages/component-admin/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..f569b273abd8938c1a205c421dff09fc32e824ec
--- /dev/null
+++ b/packages/component-admin/index.js
@@ -0,0 +1,3 @@
+module.exports = {
+  backend: () => app => require('./src/User')(app),
+}
diff --git a/packages/component-admin/package.json b/packages/component-admin/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..12bc8f99650bf87df42887c5d9d350090b7d8771
--- /dev/null
+++ b/packages/component-admin/package.json
@@ -0,0 +1,29 @@
+{
+  "name": "pubsweet-component-admin",
+  "version": "0.0.1",
+  "description": "admin component for pubsweet",
+  "license": "MIT",
+  "files": [
+    "src"
+  ],
+  "scripts": {
+    "test": "jest"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://gitlab.coko.foundation/xpub/xpub"
+  },
+  "dependencies": {
+    "body-parser": "^1.17.2"
+  },
+  "peerDependencies": {
+    "@pubsweet/logger": "^0.0.1",
+    "pubsweet": "^1.1.1",
+    "pubsweet-client": "^1.1.1",
+    "pubsweet-server": "^1.0.1"
+  },
+  "devDependencies": {
+    "jest": "^22.1.1",
+    "supertest": "^3.0.0"
+  }
+}
diff --git a/packages/component-admin/src/User.js b/packages/component-admin/src/User.js
new file mode 100644
index 0000000000000000000000000000000000000000..f72f01649615103c983a34407c02ebd314a4596e
--- /dev/null
+++ b/packages/component-admin/src/User.js
@@ -0,0 +1,150 @@
+const bodyParser = require('body-parser')
+const logger = require('@pubsweet/logger')
+const uuid = require('uuid')
+const crypto = require('crypto')
+const mailService = require('pubsweet-component-mail-service')
+
+const User = app => {
+  app.use(bodyParser.json())
+  const authBearer = app.locals.passport.authenticate('bearer', {
+    session: false,
+  })
+  app.post('/api/admin/users', authBearer, async (req, res) => {
+    const reqUser = await app.locals.models.User.find(req.user)
+    if (reqUser.admin !== true) {
+      res.status(403).json({ error: 'Unauthorized' })
+      logger.error('unauthorized request')
+      return
+    }
+    const { email, role } = req.body
+    if (email === undefined || role === undefined) {
+      res.status(400).json({ error: 'all parameters are required' })
+      logger.error('some parameters are missing')
+      return
+    }
+
+    try {
+      const user = await app.locals.models.User.findByEmail(email)
+      if (user) {
+        res.status(400).json({ error: 'User already exists' })
+        logger.error('admin tried to invite existing user')
+        return
+      }
+    } catch (e) {
+      if (e.name === 'NotFoundError') {
+        const userBody = {
+          username: uuid.v4().slice(0, 7),
+          email,
+          password: uuid.v4(),
+          roles: { role },
+          passwordResetToken: crypto.randomBytes(32).toString('hex'),
+          isConfirmed: false,
+        }
+        let newUser = new app.locals.models.User(userBody)
+        newUser = await newUser.save()
+        let emailType
+        switch (newUser.roles.role) {
+          case 'editorInChief':
+            emailType = 'invite-editor-in-chief'
+            break
+          case 'handlingEditor':
+            emailType = 'invite-handling-editor'
+            break
+          case 'reviewer':
+            emailType = 'invite-reviewer'
+            break
+          default:
+            break
+        }
+        await mailService.setupEmail(
+          newUser.email,
+          emailType,
+          newUser.passwordResetToken,
+        )
+      }
+    }
+
+    res.status(204).json()
+  })
+  app.post(
+    '/api/admin/users/password-reset',
+    bodyParser.json(),
+    async (req, res) => {
+      const {
+        token,
+        password,
+        email,
+        firstName,
+        lastName,
+        username,
+        middleName,
+        affiliation,
+        position,
+        title,
+      } = req.body
+
+      if (
+        !checkForUndefinedParams(
+          token,
+          password,
+          email,
+          firstName,
+          lastName,
+          username,
+        )
+      ) {
+        res.status(400).json({ error: 'missing required params' })
+        return
+      }
+
+      const updateFields = {
+        password,
+        firstName,
+        lastName,
+        username,
+        middleName,
+        affiliation,
+        position,
+        title,
+        isConfirmed: true,
+      }
+
+      try {
+        const user = await app.locals.models.User.findByEmail(email)
+        if (user) {
+          if (token !== user.passwordResetToken) {
+            res.status(400).json({ error: 'invalid request' })
+            logger.error('admin pw reset tokens do not match')
+            return
+          }
+
+          let newUser = Object.assign(user, updateFields, user)
+          delete newUser.passwordResetToken
+
+          newUser = await newUser.save()
+          res.status(200).json(newUser)
+        }
+      } catch (e) {
+        if (e.name === 'NotFoundError') {
+          res.status(404).json({ error: 'user not found' })
+          logger.error('admin pw reset on non-existing user')
+        } else if (e.name === 'ValidationError') {
+          res.status(400).json({ error: e.details[0].message })
+          logger.error('admin pw reset validation error')
+        }
+        res.status(400).json({ error: e })
+        logger.error(e)
+      }
+    },
+  )
+}
+
+const checkForUndefinedParams = (...params) => {
+  if (params.includes(undefined)) {
+    return false
+  }
+
+  return true
+}
+
+module.exports = User
diff --git a/packages/component-aws-ses/index.js b/packages/component-aws-ses/index.js
index 8b8d52ceca5a0d380e5e5a3a66681030ccfad18a..b54639e30fd890a999ba73bcf5ab581788aeaa82 100644
--- a/packages/component-aws-ses/index.js
+++ b/packages/component-aws-ses/index.js
@@ -1,5 +1,3 @@
 require('dotenv').config()
 
-module.exports = {
-  backend: () => app => require('./src/EmailBackend')(app),
-}
+module.exports = require('./src/EmailBackend')
diff --git a/packages/component-aws-ses/package.json b/packages/component-aws-ses/package.json
index 6267c1f10a75287b9b72fe5e0c2f369e2f6b8190..21500be0578aca6d2dfa147f4e3e5ddee0829738 100644
--- a/packages/component-aws-ses/package.json
+++ b/packages/component-aws-ses/package.json
@@ -1,6 +1,6 @@
 {
   "name": "pubsweet-components-aws-ses",
-  "version": "0.0.1",
+  "version": "0.2.0",
   "description": "xpub aws ses configured for faraday",
   "license": "MIT",
   "files": [
diff --git a/packages/component-aws-ses/src/EmailBackend.js b/packages/component-aws-ses/src/EmailBackend.js
index 33a52e0f83f0b42874701cfdfece5ff793a5805a..3cef31414720ec8cb8caf745fd84f3df4d22fa5c 100644
--- a/packages/component-aws-ses/src/EmailBackend.js
+++ b/packages/component-aws-ses/src/EmailBackend.js
@@ -1,5 +1,4 @@
 const nodemailer = require('nodemailer')
-const bodyParser = require('body-parser')
 const AWS = require('aws-sdk')
 const config = require('config')
 const _ = require('lodash')
@@ -7,36 +6,21 @@ const logger = require('@pubsweet/logger')
 
 const sesConfig = _.get(config, 'pubsweet-component-aws-ses')
 
-const EmailBackend = app => {
-  app.use(bodyParser.json())
-  const authBearer = app.locals.passport.authenticate('bearer', {
-    session: false,
-  })
-  app.post('/api/email', authBearer, async (req, res) => {
+module.exports = {
+  sendEmail: (toEmail, subject, textBody, htmlBody) => {
     AWS.config.update({
       secretAccessKey: sesConfig.secretAccessKey,
       accessKeyId: sesConfig.accessKeyId,
       region: sesConfig.region,
     })
 
-    const { email, subject, textBody, htmlBody } = req.body
-    if (
-      email === undefined ||
-      subject === undefined ||
-      textBody === undefined ||
-      htmlBody === undefined
-    ) {
-      res.status(400).json({ error: 'all parameters are required' })
-      logger.error('some parameters are missing')
-      return
-    }
     const transporter = nodemailer.createTransport({
       SES: new AWS.SES(),
     })
     transporter.sendMail(
       {
         from: sesConfig.sender,
-        to: email,
+        to: toEmail,
         subject,
         text: textBody,
         html: htmlBody,
@@ -48,8 +32,5 @@ const EmailBackend = app => {
         logger.debug(info)
       },
     )
-    res.status(204).json()
-  })
+  },
 }
-
-module.exports = EmailBackend
diff --git a/packages/component-mail-service/.gitignore b/packages/component-mail-service/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3614a810088d89d9ccaa28d82401545634874a18
--- /dev/null
+++ b/packages/component-mail-service/.gitignore
@@ -0,0 +1,8 @@
+_build/
+api/
+logs/
+node_modules/
+uploads/
+.env.*
+.env
+config/local*.*
\ No newline at end of file
diff --git a/packages/component-mail-service/README.md b/packages/component-mail-service/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/component-mail-service/index.js b/packages/component-mail-service/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..3d0b3c14535e01c151f895accdfd5e0ccfc1294b
--- /dev/null
+++ b/packages/component-mail-service/index.js
@@ -0,0 +1 @@
+module.exports = require('./src/Mail')
diff --git a/packages/component-mail-service/package.json b/packages/component-mail-service/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..09ca4ea72eafe7a7507fef3b6cd682216dad9850
--- /dev/null
+++ b/packages/component-mail-service/package.json
@@ -0,0 +1,30 @@
+{
+  "name": "pubsweet-component-mail-service",
+  "version": "0.0.1",
+  "description": "mail service component for pubsweet",
+  "license": "MIT",
+  "files": [
+    "src"
+  ],
+  "scripts": {
+    "test": "jest"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://gitlab.coko.foundation/xpub/xpub"
+  },
+  "dependencies": {
+    "body-parser": "^1.17.2",
+    "handlebars": "^4.0.11"
+  },
+  "peerDependencies": {
+    "@pubsweet/logger": "^0.0.1",
+    "pubsweet": "^1.1.1",
+    "pubsweet-client": "^1.1.1",
+    "pubsweet-server": "^1.0.1"
+  },
+  "devDependencies": {
+    "jest": "^22.1.1",
+    "supertest": "^3.0.0"
+  }
+}
diff --git a/packages/component-mail-service/src/Mail.js b/packages/component-mail-service/src/Mail.js
new file mode 100644
index 0000000000000000000000000000000000000000..23644ec833d62036e394e8f933810fad077ae486
--- /dev/null
+++ b/packages/component-mail-service/src/Mail.js
@@ -0,0 +1,50 @@
+const fs = require('fs')
+const handlebars = require('handlebars')
+const querystring = require('querystring')
+const SES = require('pubsweet-components-aws-ses')
+const config = require('config')
+
+const resetUrl = config.get('admin-reset-password.url')
+
+module.exports = {
+  setupEmail: async (email, emailType, token, comment = '') => {
+    let subject
+    const htmlFile = readFile(`${__dirname}/templates/${emailType}.html`)
+    const textFile = readFile(`${__dirname}/templates/${emailType}.txt`)
+    let replacements = {}
+    const htmlTemplate = handlebars.compile(htmlFile)
+    const textTemplate = handlebars.compile(textFile)
+
+    switch (emailType) {
+      case 'invite-editor-in-chief':
+        subject = 'Hindawi Invitation'
+        replacements = {
+          url: `${resetUrl}?${querystring.encode({
+            email,
+            token,
+          })}`,
+        }
+        break
+      default:
+        subject = 'Welcome to Hindawi!'
+        break
+    }
+
+    const htmlBody = htmlTemplate(replacements)
+    const textBody = textTemplate(replacements)
+
+    SES.sendEmail(email, subject, textBody, htmlBody)
+  },
+}
+
+const readFile = path => {
+  const file = fs.readFileSync(path, { encoding: 'utf-8' }, (err, file) => {
+    if (err) {
+      throw err
+    } else {
+      return file
+    }
+  })
+
+  return file
+}
diff --git a/packages/component-mail-service/src/templates/invite-editor-in-chief.html b/packages/component-mail-service/src/templates/invite-editor-in-chief.html
new file mode 100644
index 0000000000000000000000000000000000000000..58abc88115fe94ea3601b861c1703316e49f0446
--- /dev/null
+++ b/packages/component-mail-service/src/templates/invite-editor-in-chief.html
@@ -0,0 +1,2 @@
+<p>You have been invited</p>
+<p>Here is your url <a href="{{ url }}">PW RESET</a></p>
\ No newline at end of file
diff --git a/packages/component-mail-service/src/templates/invite-editor-in-chief.txt b/packages/component-mail-service/src/templates/invite-editor-in-chief.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d249e30cc14554c2a87ba079439bbba7d2b2d976
--- /dev/null
+++ b/packages/component-mail-service/src/templates/invite-editor-in-chief.txt
@@ -0,0 +1,2 @@
+You have been invited
+Here is your url PW REST {{ url }}
\ No newline at end of file
diff --git a/packages/xpub-faraday/config/components.json b/packages/xpub-faraday/config/components.json
index 8c34645a85924f65fc2e2d8475db8b2d05cba90c..d64253bf25516eebd779a3f72a6f728e37ef63c6 100644
--- a/packages/xpub-faraday/config/components.json
+++ b/packages/xpub-faraday/config/components.json
@@ -7,5 +7,5 @@
   "pubsweet-component-wizard",
   "pubsweet-components-faraday",
   "pubsweet-components-aws-s3",
-  "pubsweet-components-aws-ses"
+  "pubsweet-component-admin"
 ]
diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js
index 3cafabc6a12877337794cf1647cc29330eca4d59..245f1dbe7b3d34ab7c80d7399c2d000240341f51 100644
--- a/packages/xpub-faraday/config/default.js
+++ b/packages/xpub-faraday/config/default.js
@@ -60,6 +60,11 @@ module.exports = {
     region: process.env.AWS_SES_REGION,
     sender: process.env.EMAIL_SENDER,
   },
+  'admin-reset-password': {
+    url:
+      process.env.PUBSWEET_ADMIN_PASSWORD_RESET_URL ||
+      'http://localhost:3000/admin/password-reset',
+  },
   publicKeys: [
     'pubsweet-client',
     'authsome',
diff --git a/packages/xpub-faraday/config/validations.js b/packages/xpub-faraday/config/validations.js
index f7d617d3d72c0ead1b9bc4390c10a45445006929..8b98883df175275ecb57a268cf4f549d065a92c3 100644
--- a/packages/xpub-faraday/config/validations.js
+++ b/packages/xpub-faraday/config/validations.js
@@ -88,6 +88,13 @@ module.exports = {
   user: {
     name: Joi.string(), // TODO: add "name" to the login form
     roles: Joi.object(),
+    isConfirmed: Joi.boolean(),
+    firstName: Joi.string().allow(''),
+    lastName: Joi.string().allow(''),
+    middleName: Joi.string().allow(''),
+    affiliation: Joi.string().allow(''),
+    position: Joi.string().allow(''),
+    title: Joi.string().allow(''),
   },
   team: {
     group: Joi.string(),
diff --git a/packages/xpub-faraday/package.json b/packages/xpub-faraday/package.json
index fabc887cc97882eb924de656b768f103fbf52326..e3b80d41b71900568e6bb0eb5d821f06df2765f7 100644
--- a/packages/xpub-faraday/package.json
+++ b/packages/xpub-faraday/package.json
@@ -31,7 +31,7 @@
     "pubsweet-component-xpub-submit": "^0.0.2",
     "pubsweet-server": "1.0.5",
     "pubsweet-components-aws-s3": "^0.0.1",
-    "pubsweet-components-aws-ses": "^0.0.1",
+    "pubsweet-component-admin": "^0.0.1",
     "react": "^15.6.1",
     "react-dnd": "^2.5.4",
     "react-dnd-html5-backend": "^2.5.4",
diff --git a/yarn.lock b/yarn.lock
index eaeb4eea2fab94726a5a42f59a381f52f3ad1025..76654745dcb4355df780499d9a0f89f31cf8640e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4324,7 +4324,7 @@ handle-thing@^1.2.5:
   version "1.2.5"
   resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4"
 
-handlebars@^4.0.2, handlebars@^4.0.3:
+handlebars@^4.0.11, handlebars@^4.0.2, handlebars@^4.0.3:
   version "4.0.11"
   resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
   dependencies: