Skip to main content

Authentication

Formidable provides a starter authentication system for both session and jwt based applications. By default, session based authentication is enabled.

The session based authentication system enables the use of cookies, and stores the session data in memory, file or redis. While the jwt based authentication system enables the use of JWT tokens, and stores authentication data in the database.

You can find your application's authentication configuration in config/auth.imba or config/auth.ts.

Getting Started

Formidable will automatically enable authentication for you. Should you not want to use authentication in your application, you can disable it by removing Auth.routes! from the app/Resolvers/RouterServiceResolver.imba or by removing Auth.routes() from the app/Resolvers/RouterServiceResolver.ts, this will disable all authentication routes.

Database Considerations

Its important to note that you will need to create a database for your application to use authentication. If a database has been created, head over to your .env file and config/database.imba or config/database.ts config file to configure your database connection, once this is done, you can run:

node craftsman migrate:latest

This will create all the tables needed for authentication. users, password_resets and personal_access_tokens tables will be created.

Configure Client

The email and password routes require a client url to be configured. This url is prepended to the routes. To configure this url, head over to your .env and set the CLIENT_URL environment variable.

Actions

Login

Log a user in

POST http://127.0.0.1:3000/login
Content-Type: application/json

{
"email": "email-address",
"password": "password"
}

Logout

Log a user out

POST http://127.0.0.1:3000/logout

Register

Register a user

POST http://127.0.0.1:3000/register
Content-Type: application/json

{
"name": "full-name",
"email": "email-address",
"password": "password",
"password_confirmation": "password"
}

Email Verification

Verify a user's email address

POST http://127.0.0.1:3000/email/verify?email={email-address}&signature={signature}

The email address and signiture are provided in the query string and can be found in the VerifyEmail mailer that get's sent to the user. When the user clicks on the link in the email, the user will be redirected to "CLIENT_URL/email/verify?email=email-address&signature=signature".

Here, you can extract the email address and signature from the query string and verify the email address by sending a post request to "/email/verify" with the extracted email address and signature.

Resend Mail

Resend an email verification email to a user

POST http://127.0.0.1:3000/email/resend
Content-Type: application/json

{
"email": "email-address"
}

Forgot Password

Request a password reset email

POST http://127.0.0.1:3000/password/forgot
Content-Type: application/json

{
"email": "email-address"
}

Reset Password

Reset a password

POST http://127.0.0.1:3000/password/reset
Content-Type: application/json

{
"password": "password",
"password_confirmation": "password",
}

The email address and signiture are provided in the query string and can be found in the ForgotPassword mailer that get's sent to the user. When the user clicks the link in the email, the user will be redirected to {CLIENT_URL}/password/reset?email={email-address}&signature={signature}.

Here, you can extract the email address and signature from the query string and reset the user's password by sending a post request to /password/reset with the extracted email address and signature.

Authentication Events

Formidable provides a number of events that can be used to hook into your application's authentication.

beforeLogin

The beforeLogin event is fired before a user is logged in:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.beforeLogin((request, reply) => {
// Do something
})
}
}

beforeLogout

The beforeLogout event is fired before a user is logged out:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.beforeLogout((request, reply) => {
// Do something
})
}
}

beforeRegister

The beforeRegister event is fired before a user is registered:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.beforeRegister((request, reply) => {
// Do something
})
}
}

beforeVerify

The beforeVerify event is fired before a user email is verified:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.beforeVerify((request, reply) => {
// Do something
})
}
}

beforeResend

The beforeResend event is fired before a user verification email is resent:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.beforeResend((request, reply) => {
// Do something
})
}
}

beforeForgot

The beforeForgot event is fired before a user password reset email is sent:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.beforeForgot((request, reply) => {
// Do something
})
}
}

beforeReset

The beforeReset event is fired before a user password is reset:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.beforeReset((request, reply) => {
// Do something
})
}
}

onAuthenticated

The onAuthenticated event is fired after a user is logged in:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.onAuthenticated((request, reply) => {
// Do something
})
}
}

onRegistered

The onRegistered event is fired after a user is registered:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.onRegistered((request, reply) => {
// Do something
})
}
}

Custom Authentication Handlers

Formidable provides an easy way to write your own authentication handlers.

onLogin

The onLogin hook is used to handle the login process:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.onLogin((request, reply) => {
// Log the user in
})
}
}

onRegister

The onRegister hook is used to handle the registration process:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.onRegister((request, reply) => {
// Register the user
})
}
}

onForgot

The onForgot hook is used to handle the forgot password process:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.onForgot((request, reply) => {
// Send the user a password reset email
})
}
}

onReset

The onReset hook is used to handle the password reset process:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.onReset((request, reply) => {
// Reset the user's password
})
}
}

onVerification

The onVerification hook is used to handle the email verification process:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.onVerification((request, reply) => {
// Verify the user's email address
})
}
}

onEmailResend

The onEmailResend hook is used to handle the email verification resend process:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth.onEmailResend((request, reply) => {
// Resend the user's email verification email
})
}
}

Authentication Mailers

By default, Formidable provides VerifyEmail and ResetPassword mailers that can be used to send email verification and password reset emails to your users:

app/Resolvers/AppServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'
import { VerifyEmail } from '../Mail/VerifyEmail'
import { ResetPassword } from '../Mail/ResetPassword'

export class AppServiceResolver extends ServiceResolver {
boot(): void {
Auth
.verificationMailer(VerifyEmail)
.resetPasswordMailer(ResetPassword)
}
}

Protecting Routes

Formidable provides an auth middleware that can be used to require users to be logged in before accessing a route:

Route.get('ping', () => 'pong').middleware(['auth'])

JWT

If you want to use JWT tokens for authentication, you can use the jwt middleware:

app/Resolvers/RouterServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'
import { Route } from '@formidablejs/framework'

export class RouterServiceResolver extends ServiceResolver {
boot(): void {
Route.group({ middleware: 'jwt' }, () => {
Auth.routes()

require('../../routes/api')
})
}
}

When logging your users in, a JWT token will be returned in the response:

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjQ5YTNkZWY0MDkxMzFjNThjNGY3NzYwNWU2NjNmYmRmIiwiaWF0IjoxNjM4ODA0NzgyLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAifQ.A008sYS3973q-6uH2cQbgPf4Xq-v93UCvNLql0knIJ8",
"type": "Bearer",
"user": {
"name": "Donald",
"email": "donaldpakkies@gmail.com",
"email_verified_at": null,
"created_at": "2021-12-06T15:41:48.000Z",
"updated_at": "2021-12-06T15:41:48.000Z"
}
}

Now, to access /ping you can pass the JWT token in the Authorization header as a Bearer token:

POST http://127.0.0.1:3000/ping
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjQ5YTNkZWY0MDkxMzFjNThjNGY3NzYwNWU2NjNmYmRmIiwiaWF0IjoxNjM4ODA0NzgyLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAifQ.A008sYS3973q-6uH2cQbgPf4Xq-v93UCvNLql0knIJ8

Session

If you want to use sessions for authentication, you can use the session middleware:

app/Resolvers/RouterServiceResolver.ts
import { AuthService as Auth } from '@formidablejs/framework'
import { ServiceResolver } from '@formidablejs/framework'
import { Route } from '@formidablejs/framework'

export class RouterServiceResolver extends ServiceResolver
boot(): void {
Route.group({ middleware: 'session' }, () => {
Auth.routes()

require('../../routes/api')
})
}
}

When logging your users in, a new session will be created and a cookie will be set in the response. You may need to set a X-CSRF-TOKEN header in your authentication routes if you have CSRF protection enabled:

POST http://127.0.0.1:3000/login
X-CSRF-TOKEN: qpCuH2vmjhxgcDMkVnzfdV4tsdr9InVBgZzPTOw6Rt3YG8Hz-t1WbthldpuBOV3hrtUGMihiTraU
Content-Type: application/json

{
"email": "email-address"
"password": "password"
}

See CSRF Protection for more information.