Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bdf3925a3 | |||
| a299bab794 | |||
| 8460d03181 | |||
| ae88f3002a | |||
| f4745084c0 | |||
| 02387914a8 | |||
| 0da150f471 | |||
| d8e9620ad1 | |||
| c1155909be | |||
| 4ec55bed10 | |||
| b51d22f1a1 | |||
| 6b3db67a29 | |||
| 81da8ca422 | |||
| 72b3babfd8 |
@@ -1,5 +1,6 @@
|
|||||||
WEB_PROTOCOL=http
|
WEB_PROTOCOL=http
|
||||||
WEB_ADDRESS=localhost
|
WEB_ADDRESS=localhost
|
||||||
|
WEB_RESET_ADDRESS=localhost/reset
|
||||||
WEB_PORT=3200
|
WEB_PORT=3200
|
||||||
|
|
||||||
DB_HOSTNAME=database
|
DB_HOSTNAME=database
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
FROM node:15
|
FROM node:16
|
||||||
WORKDIR "/app"
|
WORKDIR "/app"
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
COPY . /app
|
|
||||||
RUN npm install --production
|
RUN npm install --production
|
||||||
|
COPY . /app
|
||||||
EXPOSE 3200
|
EXPOSE 3200
|
||||||
USER node
|
USER node
|
||||||
ENTRYPOINT ["bash", "-c"]
|
ENTRYPOINT ["bash", "-c"]
|
||||||
|
|||||||
@@ -19,9 +19,19 @@ Content-Type: application/json
|
|||||||
"password": "helloworld"
|
"password": "helloworld"
|
||||||
}
|
}
|
||||||
|
|
||||||
//DOCS: Used for validating the email address above
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
//DOCS: Used for validating the email address specified above
|
||||||
GET /auth/validation?username=example&token=12345678
|
GET /auth/validation?username=example&token=12345678
|
||||||
|
|
||||||
|
//DOCS: If the environment variable HOOK_POST_VALIDATION is set to a URL, then the server will send a GET message to that URL with the newly created account's index
|
||||||
|
GET https://{HOOK_POST_VALIDATION}?accountIndex={index}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
//DOCS: Login after validation
|
//DOCS: Login after validation
|
||||||
POST /auth/login
|
POST /auth/login
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
@@ -31,13 +41,17 @@ Content-Type: application/json
|
|||||||
"password": "helloworld"
|
"password": "helloworld"
|
||||||
}
|
}
|
||||||
|
|
||||||
//Result (access token's value is your authorization key below)
|
//DOCS: Result (access token's value is your authorization key below)
|
||||||
{
|
{
|
||||||
"accessToken": "abcde",
|
"accessToken": "abcde",
|
||||||
"refreshToken": "fghij"
|
"refreshToken": "fghij"
|
||||||
}
|
}
|
||||||
|
|
||||||
//Replace an expired authToken pair with these values
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
//DOCS: Replace an expired authToken pair with new values
|
||||||
POST /auth/token
|
POST /auth/token
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
@@ -45,6 +59,16 @@ Content-Type: application/json
|
|||||||
"token": "refreshToken"
|
"token": "refreshToken"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//DOCS: Result
|
||||||
|
{
|
||||||
|
"accessToken": "abcde",
|
||||||
|
"refreshToken": "fghij"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
//DOCS: After this is called, the refresh route will no longer work
|
//DOCS: After this is called, the refresh route will no longer work
|
||||||
DELETE /auth/logout
|
DELETE /auth/logout
|
||||||
Authorization: Bearer accessToken
|
Authorization: Bearer accessToken
|
||||||
@@ -53,21 +77,32 @@ Authorization: Bearer accessToken
|
|||||||
"token": "refreshToken"
|
"token": "refreshToken"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
//DOCS: Retreives the private account data, results vary
|
//DOCS: Retreives the private account data, results vary
|
||||||
GET /auth/account
|
GET /auth/account
|
||||||
Authorization: Bearer accessToken
|
Authorization: Bearer accessToken
|
||||||
|
|
||||||
//Result
|
|
||||||
{
|
|
||||||
"accessToken": "abcde",
|
|
||||||
"refreshToken": "fghij"
|
|
||||||
}
|
|
||||||
|
|
||||||
//DOCS: Update account data, input varies, but is always JSON
|
###
|
||||||
|
|
||||||
|
|
||||||
|
//DOCS: Update account data
|
||||||
PATCH /auth/account
|
PATCH /auth/account
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: Bearer accessToken
|
Authorization: Bearer accessToken
|
||||||
|
|
||||||
|
{
|
||||||
|
"password": "helloworld",
|
||||||
|
"contact": true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
//DOCS: Sets the timer, account will be deleted after 2 days
|
//DOCS: Sets the timer, account will be deleted after 2 days
|
||||||
DELETE /auth/account
|
DELETE /auth/account
|
||||||
Authorization: Bearer accessToken
|
Authorization: Bearer accessToken
|
||||||
@@ -76,4 +111,40 @@ Content-Type: application/json
|
|||||||
{
|
{
|
||||||
"password": "helloworld"
|
"password": "helloworld"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
//DOCS: Send the link to recover a forgotten password
|
||||||
|
POST /auth/recover
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"email": "kayneruse@gmail.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
//DOCS: Redirect the link to recover a password to the front-end
|
||||||
|
GET /auth/reset?token=<token>
|
||||||
|
|
||||||
|
//DOCS: Result
|
||||||
|
301 -> ${WEB_RESET_ADDRESS}?email=<email>&token=<token>
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
//DOCS: Resets a password for the given email, correct token is required
|
||||||
|
PATCH /auth/reset?email=<email>&token=<token>
|
||||||
|
|
||||||
|
{
|
||||||
|
"password": "password"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
```
|
```
|
||||||
|
|||||||
+5
-1
@@ -30,6 +30,8 @@ const question = (prompt, def = null) => {
|
|||||||
//project configuration
|
//project configuration
|
||||||
const appName = await question('App Name', 'auth');
|
const appName = await question('App Name', 'auth');
|
||||||
const appWebAddress = await question('Web Addr', `${appName}.example.com`);
|
const appWebAddress = await question('Web Addr', `${appName}.example.com`);
|
||||||
|
const postValidationHook = await question('Post Validation Hook', '');
|
||||||
|
const resetAddress = await question('Reset Addr', `example.com/reset`);
|
||||||
const appPort = await question('App Port', '3200');
|
const appPort = await question('App Port', '3200');
|
||||||
|
|
||||||
const appDBUser = await question('DB User', appName);
|
const appDBUser = await question('DB User', appName);
|
||||||
@@ -69,6 +71,8 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- WEB_PROTOCOL=https
|
- WEB_PROTOCOL=https
|
||||||
- WEB_ADDRESS=${appWebAddress}
|
- WEB_ADDRESS=${appWebAddress}
|
||||||
|
- HOOK_POST_VALIDATION=${postValidationHook}
|
||||||
|
- WEB_RESET_ADDRESS=${resetAddress}
|
||||||
- WEB_PORT=${appPort}
|
- WEB_PORT=${appPort}
|
||||||
- DB_HOSTNAME=database
|
- DB_HOSTNAME=database
|
||||||
- DB_DATABASE=${appName}
|
- DB_DATABASE=${appName}
|
||||||
@@ -126,7 +130,7 @@ networks:
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const dockerfile = `
|
const dockerfile = `
|
||||||
FROM node:15
|
FROM node:16
|
||||||
WORKDIR "/app"
|
WORKDIR "/app"
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm install --production
|
RUN npm install --production
|
||||||
|
|||||||
Generated
+125
-113
@@ -1,21 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "auth-server",
|
"name": "auth-server",
|
||||||
"version": "1.0.0",
|
"version": "1.4.6",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"version": "1.0.0",
|
"name": "auth-server",
|
||||||
|
"version": "1.4.6",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.19.0",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^8.6.0",
|
"dotenv": "^8.6.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"mariadb": "^2.5.4",
|
"mariadb": "^2.5.4",
|
||||||
"node-cron": "^2.0.3",
|
"node-cron": "^2.0.3",
|
||||||
|
"node-fetch": "^3.1.0",
|
||||||
"nodemailer": "^6.6.3",
|
"nodemailer": "^6.6.3",
|
||||||
"sequelize": "^6.6.5"
|
"sequelize": "^6.6.5"
|
||||||
},
|
},
|
||||||
@@ -73,68 +74,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ansi-align": {
|
"node_modules/ansi-align": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||||
"integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==",
|
"integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"string-width": "^3.0.0"
|
"string-width": "^4.1.0"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-align/node_modules/ansi-regex": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-align/node_modules/emoji-regex": {
|
|
||||||
"version": "7.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
|
||||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/ansi-align/node_modules/is-fullwidth-code-point": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-align/node_modules/string-width": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"emoji-regex": "^7.0.1",
|
|
||||||
"is-fullwidth-code-point": "^2.0.0",
|
|
||||||
"strip-ansi": "^5.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-align/node_modules/strip-ansi": {
|
|
||||||
"version": "5.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
|
||||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^4.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -502,6 +453,14 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/data-uri-to-buffer": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
@@ -687,6 +646,27 @@
|
|||||||
"node": ">= 0.10.0"
|
"node": ">= 0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fetch-blob": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-ax1Y5I9w+9+JiM+wdHkhBoxew+zG4AJ2SvAD1v1szpddUIiPERVGBxrMcB2ZqW0Y3PP8bOWYv2zqQq1Jp2kqUQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/jimmywarting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "paypal",
|
||||||
|
"url": "https://paypal.me/jimmywarting"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"web-streams-polyfill": "^3.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20 || >= 14.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
@@ -716,6 +696,17 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/formdata-polyfill": {
|
||||||
|
"version": "4.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||||
|
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||||
|
"dependencies": {
|
||||||
|
"fetch-blob": "^3.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@@ -1330,6 +1321,23 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-QU0WbIfMUjd5+MUzQOYhenAazakV7Irh1SGkWCsRzBwvm4fAhzEUaHMJ6QLP7gWT6WO9/oH2zhKMMGMuIrDyKw==",
|
||||||
|
"dependencies": {
|
||||||
|
"data-uri-to-buffer": "^4.0.0",
|
||||||
|
"fetch-blob": "^3.1.2",
|
||||||
|
"formdata-polyfill": "^4.0.10"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/node-fetch"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nodemailer": {
|
"node_modules/nodemailer": {
|
||||||
"version": "6.6.3",
|
"version": "6.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.3.tgz",
|
||||||
@@ -2080,9 +2088,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/validator": {
|
"node_modules/validator": {
|
||||||
"version": "13.6.0",
|
"version": "13.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
|
||||||
"integrity": "sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg==",
|
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
@@ -2095,6 +2103,14 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/web-streams-polyfill": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/widest-line": {
|
"node_modules/widest-line": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
||||||
@@ -2190,58 +2206,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ansi-align": {
|
"ansi-align": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||||
"integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==",
|
"integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"string-width": "^3.0.0"
|
"string-width": "^4.1.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"emoji-regex": {
|
|
||||||
"version": "7.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
|
||||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"is-fullwidth-code-point": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"string-width": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"emoji-regex": "^7.0.1",
|
|
||||||
"is-fullwidth-code-point": "^2.0.0",
|
|
||||||
"strip-ansi": "^5.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"strip-ansi": {
|
|
||||||
"version": "5.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
|
||||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"ansi-regex": "^4.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ansi-styles": {
|
"ansi-styles": {
|
||||||
@@ -2527,6 +2503,11 @@
|
|||||||
"integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
|
"integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"data-uri-to-buffer": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA=="
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
@@ -2682,6 +2663,14 @@
|
|||||||
"vary": "~1.1.2"
|
"vary": "~1.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fetch-blob": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-ax1Y5I9w+9+JiM+wdHkhBoxew+zG4AJ2SvAD1v1szpddUIiPERVGBxrMcB2ZqW0Y3PP8bOWYv2zqQq1Jp2kqUQ==",
|
||||||
|
"requires": {
|
||||||
|
"web-streams-polyfill": "^3.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fill-range": {
|
"fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
@@ -2705,6 +2694,14 @@
|
|||||||
"unpipe": "~1.0.0"
|
"unpipe": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"formdata-polyfill": {
|
||||||
|
"version": "4.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||||
|
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||||
|
"requires": {
|
||||||
|
"fetch-blob": "^3.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"forwarded": {
|
"forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@@ -3182,6 +3179,16 @@
|
|||||||
"tz-offset": "0.0.1"
|
"tz-offset": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node-fetch": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-QU0WbIfMUjd5+MUzQOYhenAazakV7Irh1SGkWCsRzBwvm4fAhzEUaHMJ6QLP7gWT6WO9/oH2zhKMMGMuIrDyKw==",
|
||||||
|
"requires": {
|
||||||
|
"data-uri-to-buffer": "^4.0.0",
|
||||||
|
"fetch-blob": "^3.1.2",
|
||||||
|
"formdata-polyfill": "^4.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nodemailer": {
|
"nodemailer": {
|
||||||
"version": "6.6.3",
|
"version": "6.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.3.tgz",
|
||||||
@@ -3744,15 +3751,20 @@
|
|||||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||||
},
|
},
|
||||||
"validator": {
|
"validator": {
|
||||||
"version": "13.6.0",
|
"version": "13.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
|
||||||
"integrity": "sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg=="
|
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw=="
|
||||||
},
|
},
|
||||||
"vary": {
|
"vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||||
},
|
},
|
||||||
|
"web-streams-polyfill": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA=="
|
||||||
|
},
|
||||||
"widest-line": {
|
"widest-line": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "auth-server",
|
"name": "auth-server",
|
||||||
"version": "1.3.2",
|
"version": "1.4.6",
|
||||||
"description": "An API centric auth server. Uses Sequelize and mariaDB by default.",
|
"description": "An API centric auth server. Uses Sequelize and mariaDB by default.",
|
||||||
"main": "server/server.js",
|
"main": "server/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -20,13 +20,13 @@
|
|||||||
"homepage": "https://github.com/krgamestudios/auth-server#readme",
|
"homepage": "https://github.com/krgamestudios/auth-server#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.19.0",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^8.6.0",
|
"dotenv": "^8.6.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"mariadb": "^2.5.4",
|
"mariadb": "^2.5.4",
|
||||||
"node-cron": "^2.0.3",
|
"node-cron": "^2.0.3",
|
||||||
|
"node-fetch": "^3.1.0",
|
||||||
"nodemailer": "^6.6.3",
|
"nodemailer": "^6.6.3",
|
||||||
"sequelize": "^6.6.5"
|
"sequelize": "^6.6.5"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const route = async (req, res) => {
|
|||||||
//forcibly logout
|
//forcibly logout
|
||||||
tokens.destroy({
|
tokens.destroy({
|
||||||
where: {
|
where: {
|
||||||
username: req.body.username || ''
|
email: req.body.email || ''
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ router.use(tokenAuth);
|
|||||||
router.use(async (req, res, next) => {
|
router.use(async (req, res, next) => {
|
||||||
const record = await accounts.findOne({
|
const record = await accounts.findOne({
|
||||||
where: {
|
where: {
|
||||||
username: req.user.username || ''
|
email: req.user.email || ''
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -14,16 +14,21 @@ router.post('/login', require('./login'));
|
|||||||
//refresh token
|
//refresh token
|
||||||
router.post('/token', require('./token'));
|
router.post('/token', require('./token'));
|
||||||
|
|
||||||
|
//password recover and reset
|
||||||
|
router.post('/recover', require('./password-recover'));
|
||||||
|
router.get('/reset', require('./password-redirect'));
|
||||||
|
router.patch('/reset', require('./password-reset'));
|
||||||
|
|
||||||
|
//logouts allowed when banned, and when the token itself is invalid
|
||||||
|
router.delete('/logout', require('./logout'));
|
||||||
|
|
||||||
//middleware
|
//middleware
|
||||||
router.use(tokenAuth);
|
router.use(tokenAuth);
|
||||||
|
|
||||||
//logouts allowed when banned, still needs tokens
|
|
||||||
router.delete('/logout', require('./logout'));
|
|
||||||
|
|
||||||
router.use(async (req, res, next) => {
|
router.use(async (req, res, next) => {
|
||||||
const record = await accounts.findOne({
|
const record = await accounts.findOne({
|
||||||
where: {
|
where: {
|
||||||
username: req.user.username || ''
|
email: req.user.email || ''
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const utils = require('util');
|
|||||||
const bcrypt = require('bcryptjs');
|
const bcrypt = require('bcryptjs');
|
||||||
|
|
||||||
const { accounts } = require('../database/models');
|
const { accounts } = require('../database/models');
|
||||||
const generate = require('../utilities/token-generate');
|
const tokenGenerate = require('../utilities/token-generate');
|
||||||
|
|
||||||
//utilities
|
//utilities
|
||||||
const validateEmail = require('../utilities/validate-email');
|
const validateEmail = require('../utilities/validate-email');
|
||||||
@@ -48,10 +48,10 @@ const route = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//generate the JWT
|
//generate the JWT
|
||||||
const tokens = generate(account.index, account.username, account.type, account.admin, account.mod);
|
const token = tokenGenerate(account.index, account.email, account.username, account.type, account.admin, account.mod);
|
||||||
|
|
||||||
//finally
|
//finally
|
||||||
res.status(200).json(tokens);
|
res.status(200).json(token);
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateDetails = async (body) => {
|
const validateDetails = async (body) => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
const destroy = require('../utilities/token-destroy');
|
const tokenDestroy = require('../utilities/token-destroy');
|
||||||
|
|
||||||
//auth/logout
|
//auth/logout
|
||||||
const route = (req, res) => {
|
const route = (req, res) => {
|
||||||
destroy(req.body.token);
|
tokenDestroy(req.body.token);
|
||||||
|
|
||||||
return res.status(200).end();
|
return res.status(200).end();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
//libraries
|
||||||
|
const nodemailer = require('nodemailer');
|
||||||
|
|
||||||
|
const { accounts, recovery } = require('../database/models');
|
||||||
|
|
||||||
|
//utilities
|
||||||
|
const uuid = require('../utilities/uuid');
|
||||||
|
const validateEmail = require('../utilities/validate-email');
|
||||||
|
|
||||||
|
//auth/recover
|
||||||
|
const route = async (req, res) => {
|
||||||
|
//validate details
|
||||||
|
const validateErr = await validateDetails(req.body);
|
||||||
|
if (validateErr) {
|
||||||
|
return res.status(401).end(validateErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//recovery token
|
||||||
|
const token = uuid(32);
|
||||||
|
|
||||||
|
//send the recovery email
|
||||||
|
const emailErr = await sendRecoveryEmail(req.body.email, token);
|
||||||
|
if (emailErr) {
|
||||||
|
return res.status(500).send(emailErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//save the token
|
||||||
|
await recovery.upsert({
|
||||||
|
email: req.body.email,
|
||||||
|
token: token
|
||||||
|
});
|
||||||
|
|
||||||
|
//finally
|
||||||
|
res.status(200).send("Recovery email sent!");
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateDetails = async (body) => {
|
||||||
|
//basic formatting
|
||||||
|
if (!validateEmail(body.email)) {
|
||||||
|
return 'Invalid email';
|
||||||
|
}
|
||||||
|
|
||||||
|
//check for existing email
|
||||||
|
const emailRecord = await accounts.findOne({
|
||||||
|
where: {
|
||||||
|
email: body.email
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!emailRecord) {
|
||||||
|
return 'Invalid email';
|
||||||
|
}
|
||||||
|
|
||||||
|
//OK
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendRecoveryEmail = async (email, token) => {
|
||||||
|
const addr = `${process.env.WEB_PROTOCOL}://${process.env.WEB_ADDRESS}/auth/reset?token=${token}`;
|
||||||
|
const msg = `Hello,
|
||||||
|
|
||||||
|
Please visit the following link to reset your password: ${addr}
|
||||||
|
|
||||||
|
If you did not request a password reset, you can safely ignore this message.
|
||||||
|
`;
|
||||||
|
|
||||||
|
let transporter, info;
|
||||||
|
|
||||||
|
//what exactly is a transport?
|
||||||
|
try {
|
||||||
|
transporter = nodemailer.createTransport({
|
||||||
|
host: process.env.MAIL_SMTP,
|
||||||
|
port: 465,
|
||||||
|
secure: true,
|
||||||
|
auth: {
|
||||||
|
user: process.env.MAIL_USERNAME,
|
||||||
|
pass: process.env.MAIL_PASSWORD
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
return `failed to create a mail transport: ${e}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send mail with defined transport object
|
||||||
|
try {
|
||||||
|
info = await transporter.sendMail({
|
||||||
|
from: `recovery@${process.env.WEB_ADDRESS}`, //WARNING: google overwrites this
|
||||||
|
to: email,
|
||||||
|
subject: 'Password Recovery',
|
||||||
|
text: msg
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
return `failed to send validation mail: ${e}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.accepted[0] != email) {
|
||||||
|
return 'recovery email failed to send';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = route;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
const { accounts, recovery } = require('../database/models');
|
||||||
|
|
||||||
|
//auth/reset
|
||||||
|
const route = async (req, res) => {
|
||||||
|
//verify the recovery record exists
|
||||||
|
const record = await recovery.findOne({
|
||||||
|
where: {
|
||||||
|
token: req.query.token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!record) {
|
||||||
|
return res.status(401).end('Failed to recover a password');
|
||||||
|
}
|
||||||
|
|
||||||
|
//redirect to the front-end
|
||||||
|
res.redirect(`${process.env.WEB_PROTOCOL}://${process.env.WEB_RESET_ADDRESS}?email=${record.email}&token=${record.token}`);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = route;
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
//libraries
|
||||||
|
const bcrypt = require('bcryptjs');
|
||||||
|
|
||||||
|
const { accounts, recovery } = require('../database/models');
|
||||||
|
|
||||||
|
//auth/reset
|
||||||
|
const route = async (req, res) => {
|
||||||
|
//validate the given details
|
||||||
|
const validateErr = await validateDetails(req.query, req.body);
|
||||||
|
if (validateErr) {
|
||||||
|
return res.status(401).send(validateErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//generate the password hash
|
||||||
|
const hash = await bcrypt.hash(req.body.password, await bcrypt.genSalt(11));
|
||||||
|
|
||||||
|
//update the account data
|
||||||
|
await accounts.update({
|
||||||
|
hash: hash
|
||||||
|
}, {
|
||||||
|
where: {
|
||||||
|
email: req.query.email
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//delete from the recovery table
|
||||||
|
await recovery.destroy({
|
||||||
|
where: {
|
||||||
|
email: req.query.email
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).end();
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateDetails = async (query, body) => {
|
||||||
|
//verify the recovery record exists
|
||||||
|
const record = await recovery.findOne({
|
||||||
|
where: {
|
||||||
|
email: query.email,
|
||||||
|
token: query.token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!record) {
|
||||||
|
return 'Failed to recover a password';
|
||||||
|
}
|
||||||
|
|
||||||
|
//validate password
|
||||||
|
if (!body.password) {
|
||||||
|
return 'Missing password';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body.password.length < 8) {
|
||||||
|
return 'Password too short';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = route;
|
||||||
@@ -6,7 +6,6 @@ const Op = Sequelize.Op;
|
|||||||
|
|
||||||
const { accounts, pendingSignups } = require('../database/models');
|
const { accounts, pendingSignups } = require('../database/models');
|
||||||
|
|
||||||
|
|
||||||
//utilities
|
//utilities
|
||||||
const uuid = require('../utilities/uuid');
|
const uuid = require('../utilities/uuid');
|
||||||
const validateEmail = require('../utilities/validate-email');
|
const validateEmail = require('../utilities/validate-email');
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
const refresh = require('../utilities/token-refresh');
|
const tokenRefresh = require('../utilities/token-refresh');
|
||||||
|
|
||||||
//auth/token
|
//auth/token
|
||||||
module.exports = async (req, res) => {
|
module.exports = async (req, res) => {
|
||||||
const refreshToken = req.body.token;
|
const refreshToken = req.body.token;
|
||||||
|
|
||||||
return refresh(refreshToken, (err, tokens) => {
|
return tokenRefresh(refreshToken, (err, token) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(err).end();
|
return res.status(err).end();
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send(tokens);
|
return res.status(200).send(token);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
const { pendingSignups, accounts } = require('../database/models');
|
const { pendingSignups, accounts } = require('../database/models');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
//auth/validation
|
//auth/validation
|
||||||
const route = async (req, res) => {
|
const route = async (req, res) => {
|
||||||
@@ -19,7 +20,7 @@ const route = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//move data to the accounts table
|
//move data to the accounts table
|
||||||
accounts.create({
|
const [account] = await accounts.upsert({
|
||||||
email: info.email,
|
email: info.email,
|
||||||
username: info.username,
|
username: info.username,
|
||||||
hash: info.hash,
|
hash: info.hash,
|
||||||
@@ -35,6 +36,21 @@ const route = async (req, res) => {
|
|||||||
|
|
||||||
//finally
|
//finally
|
||||||
res.status(200).send('Validation succeeded!');
|
res.status(200).send('Validation succeeded!');
|
||||||
|
|
||||||
|
//post-validation hook
|
||||||
|
if (process.env.HOOK_POST_VALIDATION) {
|
||||||
|
const probe = await fetch(`https://${process.env.HOOK_POST_VALIDATION}?accountIndex=${account.index}`);
|
||||||
|
|
||||||
|
if (!probe.ok) {
|
||||||
|
console.error('Could not probe the post validation hook');
|
||||||
|
} else {
|
||||||
|
console.log('Validation hook probe successful'); //TODO: remove this
|
||||||
|
}
|
||||||
|
|
||||||
|
//discard the result
|
||||||
|
} else {
|
||||||
|
console.log('No validation hook'); //TODO: remove this
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = route;
|
module.exports = route;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
tokens: require('./tokens'),
|
tokens: require('./tokens'),
|
||||||
accounts: require('./accounts'),
|
accounts: require('./accounts'),
|
||||||
pendingSignups: require('./pending-signups')
|
pendingSignups: require('./pending-signups'),
|
||||||
|
recovery: require('./recovery')
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
const sequelize = require('..');
|
||||||
|
|
||||||
|
module.exports = sequelize.define('recovery', {
|
||||||
|
token: 'varchar(320)',
|
||||||
|
email: 'varchar(320)'
|
||||||
|
});
|
||||||
@@ -3,5 +3,5 @@ const sequelize = require('..');
|
|||||||
|
|
||||||
module.exports = sequelize.define('tokens', {
|
module.exports = sequelize.define('tokens', {
|
||||||
token: 'varchar(320)',
|
token: 'varchar(320)',
|
||||||
username: 'varchar(320)' //TODO: why username?
|
email: 'varchar(320)'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,19 +2,20 @@ const jwt = require('jsonwebtoken');
|
|||||||
const { tokens } = require('../database/models');
|
const { tokens } = require('../database/models');
|
||||||
|
|
||||||
//generates a JWT token based on the given arguments
|
//generates a JWT token based on the given arguments
|
||||||
module.exports = (index, username, type, admin, mod) => {
|
module.exports = (index, email, username, type, admin, mod) => {
|
||||||
const content = {
|
const content = {
|
||||||
index,
|
index,
|
||||||
|
email,
|
||||||
username,
|
username,
|
||||||
type,
|
type,
|
||||||
admin,
|
admin,
|
||||||
mod,
|
mod,
|
||||||
};
|
};
|
||||||
|
|
||||||
const accessToken = jwt.sign(content, process.env.SECRET_ACCESS, { expiresIn: '10m' });
|
const accessToken = jwt.sign(content, process.env.SECRET_ACCESS, { expiresIn: '10m', issuer: 'auth' });
|
||||||
const refreshToken = jwt.sign(content, process.env.SECRET_REFRESH, { expiresIn: '30d' });
|
const refreshToken = jwt.sign(content, process.env.SECRET_REFRESH, { expiresIn: '30d', issuer: 'auth' });
|
||||||
|
|
||||||
tokens.create({ token: refreshToken, username: username });
|
tokens.create({ token: refreshToken, email: email });
|
||||||
|
|
||||||
return { accessToken, refreshToken };
|
return { accessToken, refreshToken };
|
||||||
};
|
};
|
||||||
@@ -4,12 +4,12 @@ const { tokens } = require('../database/models');
|
|||||||
const generate = require('./token-generate');
|
const generate = require('./token-generate');
|
||||||
const destroy = require('./token-destroy');
|
const destroy = require('./token-destroy');
|
||||||
|
|
||||||
module.exports = (token, callback) => {
|
module.exports = async (token, callback) => {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return callback(401);
|
return callback(401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenRecord = tokens.findOne({
|
const tokenRecord = await tokens.findOne({
|
||||||
where: {
|
where: {
|
||||||
token: token || ''
|
token: token || ''
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ module.exports = (token, callback) => {
|
|||||||
return callback(403);
|
return callback(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = generate(user.index, user.username, user.type, user.admin, user.mod);
|
const result = generate(user.index, user.email, user.username, user.type, user.admin, user.mod);
|
||||||
|
|
||||||
destroy(token);
|
destroy(token);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `accounts` CHANGE `id` `index` INT( 11 ) NOT NULL AUTO_INCREMENT;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE tokens;
|
||||||
+4
-2
@@ -15,8 +15,8 @@ POST http://127.0.0.1:3200/auth/login HTTP/1.1
|
|||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"email": "kayneruse@gmail.com",
|
"email": "admin@example.com",
|
||||||
"password": "helloworld"
|
"password": "password"
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
@@ -66,3 +66,5 @@ Content-Type: application/json
|
|||||||
{
|
{
|
||||||
"password": "helloworld"
|
"password": "helloworld"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|||||||
Reference in New Issue
Block a user