diff --git a/app/Controllers/Http/AuthController.ts b/app/Controllers/Http/AuthController.ts index c15cdf5..c97304a 100644 --- a/app/Controllers/Http/AuthController.ts +++ b/app/Controllers/Http/AuthController.ts @@ -1,5 +1,7 @@ +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' type AuthSuccResult = { notification: { @@ -7,7 +9,8 @@ type AuthSuccResult = { text: string } user: string, - role: string + role: string, + token: string } type AuthErrResult = { @@ -20,14 +23,14 @@ type AuthErrResult = { export default class AuthController { - + /**TODO #3 implement rememberMe function */ public async login({auth, request, response}: HttpContextContract) { const username = request.body().username const password = request.body().password - const rememberMe = request.body().rememberMe ?? false + // const rememberMe = request.body().rememberMe ?? false try { - await auth.attempt(username, password, rememberMe) + const token = await auth.use('api').attempt(username, password) const result : AuthSuccResult = { notification: { @@ -35,7 +38,8 @@ export default class AuthController { text: 'Login successful!' }, user: auth.user?.username ?? '', - role: auth.user?.role ?? '' + role: auth.user?.role ?? '', + token: token.token } return response.ok(result) @@ -53,8 +57,13 @@ export default class AuthController { } public async logout({auth, response}: HttpContextContract) { - await auth.logout() - - return response.ok('Logged out successfully') + Logger.info("entering logout function") + try { + await auth.use('api').revoke() + return response.ok('Logged out successfully') + } + catch(error) { + Logger.error(error.message) + } } } diff --git a/config/auth.ts b/config/auth.ts index a0c84a2..6d2eb3a 100644 --- a/config/auth.ts +++ b/config/auth.ts @@ -17,20 +17,43 @@ import { AuthConfig } from '@ioc:Adonis/Addons/Auth' | */ const authConfig: AuthConfig = { - guard: 'web', + guard: 'api', guards: { /* |-------------------------------------------------------------------------- - | Web Guard + | OAT Guard |-------------------------------------------------------------------------- | - | Web guard uses classic old school sessions for authenticating users. - | If you are building a standard web application, it is recommended to - | use web guard with session driver + | OAT (Opaque access tokens) guard uses database backed tokens to authenticate + | HTTP request. This guard DOES NOT rely on sessions or cookies and uses + | Authorization header value for authentication. + | + | Use this guard to authenticate mobile apps or web clients that cannot rely + | on cookies/sessions. | */ - web: { - driver: 'session', + api: { + driver: 'oat', + + /* + |-------------------------------------------------------------------------- + | Tokens provider + |-------------------------------------------------------------------------- + | + | Uses SQL database for managing tokens. Use the "database" driver, when + | tokens are the secondary mode of authentication. + | For example: The Github personal tokens + | + | The foreignKey column is used to make the relationship between the user + | and the token. You are free to use any column name here. + | + */ + tokenProvider: { + type: 'api', + driver: 'database', + table: 'api_tokens', + foreignKey: 'user_id', + }, provider: { /* diff --git a/contracts/auth.ts b/contracts/auth.ts index c679bc0..278129c 100644 --- a/contracts/auth.ts +++ b/contracts/auth.ts @@ -57,16 +57,16 @@ declare module '@ioc:Adonis/Addons/Auth' { interface GuardsList { /* |-------------------------------------------------------------------------- - | Web Guard + | OAT Guard |-------------------------------------------------------------------------- | - | The web guard uses sessions for maintaining user login state. It uses - | the `user` provider for fetching user details. + | OAT, stands for (Opaque access tokens) guard uses database backed tokens + | to authenticate requests. | */ - web: { - implementation: SessionGuardContract<'user', 'web'> - config: SessionGuardConfig<'user'> + api: { + implementation: OATGuardContract<'user', 'api'> + config: OATGuardConfig<'user'> } } } diff --git a/database/migrations/1634871433879_api_tokens.ts b/database/migrations/1634871433879_api_tokens.ts new file mode 100644 index 0000000..d26751e --- /dev/null +++ b/database/migrations/1634871433879_api_tokens.ts @@ -0,0 +1,25 @@ +import BaseSchema from '@ioc:Adonis/Lucid/Schema' + +export default class ApiTokens extends BaseSchema { + protected tableName = 'api_tokens' + + public async up() { + this.schema.createTable(this.tableName, (table) => { + table.increments('id').primary() + table.integer('user_id').unsigned().references('id').inTable('users').onDelete('CASCADE') + table.string('name').notNullable() + table.string('type').notNullable() + table.string('token', 64).notNullable().unique() + + /** + * Uses timestampz for PostgreSQL and DATETIME2 for MSSQL + */ + table.timestamp('expires_at', { useTz: true }).nullable() + table.timestamp('created_at', { useTz: true }).notNullable() + }) + } + + public async down() { + this.schema.dropTable(this.tableName) + } +} diff --git a/start/routes.ts b/start/routes.ts index ba42632..b4d7d2a 100644 --- a/start/routes.ts +++ b/start/routes.ts @@ -26,7 +26,7 @@ Route.group(() => { .prefix('api/v1') Route.group(() => { - Route.get('logout', 'AuthController.logout').as('logout') + Route.post('logout', 'AuthController.logout').as('logout') Route.resource('employees', 'EmployeesController').apiOnly() })