feat(auth): add session login page, API key guard, and remove orphan SQL migrations

- Add session-based authentication for web-UI (Login.vue, AuthController)
- Add AuthSessionMiddleware: redirects unauthenticated users to /login
- Add ApiKeyMiddleware: validates X-Api-Key header on all /api/v1 routes
- Add BasicAuthMiddleware: registered for future M2M use
- Use timingSafeEqual for all credential comparisons (timing attack prevention)
- Regenerate session ID on login (session fixation prevention)
- Validate redirect target after login (open redirect prevention)
- Require WEBUI_USERNAME, WEBUI_PASSWORD, API_KEY as mandatory env vars
- Share auth.username/authenticated via Inertia shared props
- Add user dropdown in navbar (avatar icon + username + logout)
- Add login i18n keys in fr.json and en.json
- Remove orphan SQL migrations (users, auth_access_tokens) and @adonisjs/lucid
This commit is contained in:
Kevin L'Hereec committed 2026-07-03 13:59:05 +02:00
1 parent 198f15ec9b
commit 991173aa05
18 files changed
+477 -482

No files matched your search

+12 -2
View File
@@ -1,4 +1,11 @@
import { Db, Collection, ObjectId, Filter, UpdateFilter, Sort } from 'mongodb'
import {
type Db,
type Collection,
ObjectId,
type Filter,
type UpdateFilter,
type Sort,
} from 'mongodb'
import { getMongoDb } from '#providers/mongodb_provider'
export abstract class BaseModel {
@@ -16,7 +23,10 @@ export abstract class BaseModel {
return this.getDb().collection(this.collectionName)
}
static async find(this: { collectionName: string; getDb(): Db }, filter: Filter<any> = {}): Promise<any[]> {
static async find(
this: { collectionName: string; getDb(): Db },
filter: Filter<any> = {}
): Promise<any[]> {
const collection = this.getDb().collection(this.collectionName)
return collection.find(filter).toArray()
}
@@ -1,21 +0,0 @@
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
protected tableName = 'users'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').notNullable()
table.string('full_name').nullable()
table.string('email', 254).notNullable().unique()
table.string('password').notNullable()
table.timestamp('created_at').notNullable()
table.timestamp('updated_at').nullable()
})
}
async down() {
this.schema.dropTable(this.tableName)
}
}
@@ -1,31 +0,0 @@
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
protected tableName = 'auth_access_tokens'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id')
table
.integer('tokenable_id')
.notNullable()
.unsigned()
.references('id')
.inTable('users')
.onDelete('CASCADE')
table.string('type').notNullable()
table.string('name').nullable()
table.string('hash').notNullable()
table.text('abilities').notNullable()
table.timestamp('created_at')
table.timestamp('updated_at')
table.timestamp('last_used_at').nullable()
table.timestamp('expires_at').nullable()
})
}
async down() {
this.schema.dropTable(this.tableName)
}
}