diff --git a/app/Controllers/Http/AuthController.ts b/app/Controllers/Http/AuthController.ts index c97304a..cb986a4 100644 --- a/app/Controllers/Http/AuthController.ts +++ b/app/Controllers/Http/AuthController.ts @@ -1,7 +1,8 @@ -import { OpaqueTokenContract } from '@ioc:Adonis/Addons/Auth' import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' import Logger from '@ioc:Adonis/Core/Logger' -import User from 'App/Models/User' +import Nonce from 'App/Models/Nonce' +import Database from '@ioc:Adonis/Lucid/Database' +import { DateTime } from 'luxon' type AuthSuccResult = { notification: { @@ -66,4 +67,30 @@ export default class AuthController { Logger.error(error.message) } } + + public async nonce(ctx: HttpContextContract) { + const requestId = ctx.request.header('X-REQUEST-ID') + + try { + this.deleteExpiredNonces() + + const nonce = await Nonce.create({ + requestId: requestId + }) + + return nonce.nonce + } + catch(err){ + return ctx.response.forbidden(err) + } + } + + private async deleteExpiredNonces(){ + try { + await Database.from('nonces').where('expiry_date', '<', DateTime.now().toFormat('yyyy-MM-dd HH:mm:ss')).delete() + } + catch(err){ + throw err + } + } } diff --git a/app/Models/Nonce.ts b/app/Models/Nonce.ts new file mode 100644 index 0000000..4ae8eea --- /dev/null +++ b/app/Models/Nonce.ts @@ -0,0 +1,29 @@ +import { DateTime } from 'luxon' +import { BaseModel, beforeCreate, column } from '@ioc:Adonis/Lucid/Orm' +import { v4 as uuidv4 } from 'uuid' + +export default class Nonce extends BaseModel { + public static selfAssignPrimaryKey = true + + @column.dateTime({ autoCreate: true }) + public createdAt: DateTime + + @column.dateTime({ autoCreate: true, autoUpdate: true }) + public updatedAt: DateTime + + @column.dateTime() + public expiryDate : DateTime; + + @column() + public nonce : string; + + @column({isPrimary: true}) + public requestId: string; + + @beforeCreate() + public static generateNonce( nonce : Nonce) { + nonce.nonce = uuidv4() + nonce.expiryDate = DateTime.now().plus({minutes: 10}) + } + +} diff --git a/database/migrations/1634969878254_nonces.ts b/database/migrations/1634969878254_nonces.ts new file mode 100644 index 0000000..0d7b2f0 --- /dev/null +++ b/database/migrations/1634969878254_nonces.ts @@ -0,0 +1,22 @@ +import BaseSchema from '@ioc:Adonis/Lucid/Schema' + +export default class Nonces extends BaseSchema { + protected tableName = 'nonces' + + public async up () { + this.schema.createTable(this.tableName, (table) => { + /** + * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL + */ + table.timestamp('created_at', { useTz: true }) + table.timestamp('updated_at', { useTz: true }) + table.timestamp('expiry_date', { useTz: true}).notNullable() + table.string('request_id').unique().notNullable() + table.string('nonce').unique().notNullable() + }) + } + + public async down () { + this.schema.dropTable(this.tableName) + } +} diff --git a/package-lock.json b/package-lock.json index d27d372..6ec3387 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,10 +19,12 @@ "proxy-addr": "^2.0.7", "reflect-metadata": "^0.1.13", "source-map-support": "^0.5.20", - "sqlite3": "^5.0.2" + "sqlite3": "^5.0.2", + "uuid": "^8.3.2" }, "devDependencies": { "@adonisjs/assembler": "^5.3.7", + "@types/uuid": "^8.3.1", "adonis-preset-ts": "^2.1.0", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", @@ -978,6 +980,12 @@ "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==", "dev": true }, + "node_modules/@types/uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", + "dev": true + }, "node_modules/@types/validator": { "version": "13.6.3", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.6.3.tgz", @@ -6704,6 +6712,16 @@ "node": ">=0.6" } }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "optional": true, + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/require-all": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/require-all/-/require-all-3.0.0.tgz", @@ -8139,13 +8157,11 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "optional": true, + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "bin": { - "uuid": "bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache": { @@ -9089,6 +9105,12 @@ "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==", "dev": true }, + "@types/uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", + "dev": true + }, "@types/validator": { "version": "13.6.3", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.6.3.tgz", @@ -13459,6 +13481,12 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "optional": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "optional": true } } }, @@ -14576,10 +14604,9 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "optional": true + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { "version": "2.3.0", diff --git a/package.json b/package.json index abe3be6..9069ea9 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "devDependencies": { "@adonisjs/assembler": "^5.3.7", + "@types/uuid": "^8.3.1", "adonis-preset-ts": "^2.1.0", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", @@ -34,6 +35,7 @@ "proxy-addr": "^2.0.7", "reflect-metadata": "^0.1.13", "source-map-support": "^0.5.20", - "sqlite3": "^5.0.2" + "sqlite3": "^5.0.2", + "uuid": "^8.3.2" } } diff --git a/start/routes.ts b/start/routes.ts index b4d7d2a..e4d08a8 100644 --- a/start/routes.ts +++ b/start/routes.ts @@ -22,6 +22,7 @@ import Route from '@ioc:Adonis/Core/Route' Route.group(() => { Route.post('login', 'AuthController.login').as('login') + Route.get('nonce', 'AuthController.nonce').as('nonce') }) .prefix('api/v1')