⏰ Chapters
- Creating Our AdonisJS 6 Project
- Creating Our Vue 3 Project
- Opening Our Projects
- Setting Up Our AdonisJS 6 Project
- Creating Our Auth Controller & Validator
- Defining Our Register & Login Validators
- Registering A User
- User Access Tokens Provider
- Logging In A User
- Logging Out A User
- Getting An Authenticated Users Details
- Defining Our Auth Routes
- Creating A Register Page & Form
- Creating A Login Page & Form
- Defining Register & Login Page Routes
- Creating An Auth Pinia Store
- Creating An API Helper Function
- Authenticating A User In Our Pinia Store
- Auth Store Login & Register Methods
- Auth Store Logout Method
- Auth Store Get User Details
- Register & Login A User On Form Submit
- Auth User Details & Logout Form
- Testing Our Authentication Flow
AdonisJS 6 Access Token Authentication in 20 Minutes
In this lesson, we'll cover how to implement access token authentication, using opaque tokens, in AdonisJS 6. We'll also take a look at what this would look like on the frontend via a Vue 3 app using Pinia
- Author
- Tom Gobich
- Published
- May 21
- Duration
- 20m 8s
Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.
Adocasts
Burlington, KY
Transcript
AdonisJS 6 Access Token Authentication in 20 Minutes
-
(upbeat music)
-
When you can't use cookies,
-
that's when access tokens become a great option
-
for authentication.
-
To get ourselves started,
-
let's go ahead and make a directory.
-
So make dir, as we're gonna have two separate projects,
-
our backend and then a separate frontend.
-
We'll call this folder access token authentication,
-
and let's go ahead and CD into that folder.
-
Once we're in here, we can go ahead and npm init,
-
AdonisJS at latest,
-
our new AdonisJS project that we'll call our backend.
-
We'll be using this project as an API,
-
so we'll select the API starter kit,
-
and we'll be using access tokens for authentication here.
-
Again, if you do have cookies available,
-
session authentication is gonna be
-
much more straightforward,
-
but we'll move forward with access token here today.
-
I use Postgres for my database,
-
but feel free to select whichever one's applicable to you,
-
and let's have a go ahead and install our dependencies.
-
While it's doing that,
-
I'm gonna create a brand new tab,
-
and within our same access token authentication folder,
-
let's create a brand new frontend app.
-
For this, I'll be using Vue.
-
So we'll npm create Vue at latest,
-
and I'll call this the frontend.
-
Go ahead and run that.
-
I'm gonna pass on TypeScript for now, as well as JSX.
-
We'll go ahead and add Vue router and Pina.
-
I'll skip Vitus.
-
We won't be doing any testing here today.
-
Skip ESLint and the dev tools for right now.
-
Feel free to select any of those
-
applicable to your use case though.
-
Go ahead and cd into our frontend here,
-
and run npm install.
-
We now have our frontend set up,
-
so I'm gonna clear out of that and boot it up.
-
So npm run dev,
-
and let's jump back over to our AdonisJS tab.
-
Looks like our project created successfully here.
-
So let's go ahead and cd into our backend.
-
We're gonna need to migrate before we start this up.
-
So let's go ahead and get both of these projects open
-
within our text editor.
-
This blue tinted one here, I'm gonna use as our frontend.
-
So hit File, Open Folder,
-
dime in the code,
-
jump into our access token authentication,
-
and I'll select frontend for this blue one.
-
Cool, so here we have our Vue app.
-
Now I'll jump over to our normal looking Visual Studio code
-
that we usually use, File, Open Folder,
-
and for this one, we'll open up our backend,
-
which will be our AdonisJS project.
-
As you can see, we have missing environment variables
-
to take care of, so we'll go ahead and jump
-
into our .env file and take care of that.
-
All we need to worry about for right now
-
is our db_password and db_database.
-
I have a test database that I'd like to reuse,
-
but do apply your applicable database name there
-
or create a new one if you need to.
-
Then we'll enter in our Postgres user's password.
-
Mine just happens to be password.
-
Okay, with that set up,
-
the API and access token starter kit
-
that we used for AdonisJS has us set up
-
with a create_users table that will define our users
-
with a full name, email, password,
-
as well as an ID and created_at,
-
as well as an access_tokens table
-
that has an ID, a tokenable ID,
-
that will relate back to our users,
-
type name, hash, abilities, created_at,
-
updated_at, last_used, and expires.
-
Abilities here are optional,
-
and you can specify them
-
whenever you're creating your token.
-
We're gonna be ignoring them here today
-
as we're just covering the basics of Auth.
-
But do note that they exist
-
and the documentation covers the difference
-
between these abilities and bouncer abilities.
-
We can jump back into our terminal
-
and within our backend, we can run node as migration run.
-
Now, since I am reusing a database
-
that already has stuff applied within it,
-
I'm gonna go ahead and have AdonisJS remove everything
-
that I have preexisting inside of this database
-
and then run my migrations by using migration fresh.
-
And as you can see, that dropped any tables
-
that I had within this database
-
and just migrated forward.
-
Again, if you're working with a brand new database,
-
you just need to run migration run.
-
Cool, so next let's go ahead
-
and create ourselves an Auth controller.
-
So node ace make controller, call this Auth,
-
keep it singular with hyphen S,
-
and we'll add a register, login, logout, in me methods
-
within this controller.
-
And then while we're here,
-
let's go ahead and make a validator as well.
-
So node ace make validator,
-
and we'll just call this Auth.
-
All right, now we're ready to dive into our app
-
and let's go ahead and do our validator first.
-
So we'll dive into here
-
and we're gonna want one validator for registration
-
and one for login.
-
So export const our register validator
-
equals vine compile vine object.
-
We will have an email property
-
of type vine string email,
-
and we can have it normalize that email as well,
-
just using default normalizations.
-
And we also want this to be unique.
-
So we'll have async DB with a callback function,
-
do const match equals await DB from users,
-
select just the ID where email is,
-
and let's grab the value as well.
-
This is the value that the user is trying to register with,
-
pass that into there.
-
And we just care about whether or not we have any results.
-
So we'll just check for the first
-
and return an inverse or match.
-
So if we do have a match,
-
we'll wanna return false here
-
as our unique validation has failed.
-
And if we don't have a match,
-
then we wanna return true
-
as our unique validation is approved.
-
And then we're gonna have a password field here as well.
-
Now we're gonna want the same password validation
-
between both our register and our login validator.
-
So we can define that in one spot,
-
just above both of these as const password
-
or password role equals vine string.
-
And let's just apply a min length of eight for now.
-
Cool, so we can scroll down a little bit here
-
and let's export const login validator equals vine,
-
compile vine object.
-
We'll have our email, vine string email,
-
and we'll go ahead and normalize that there too.
-
We don't wanna check for uniqueness here
-
because this is during our login.
-
So the user already exists inside of the database.
-
And then we just need to add in our password.
-
We'll give that a save, that'll auto format.
-
If you have limp fix on save applied.
-
And now we're ready to dive into our controllers,
-
auth controller.
-
So we'll start with our registration
-
and we're gonna need our request
-
and we'll grab our const data
-
from await request dot validate using our register validator
-
and then we'll need to create a user with that data.
-
So const user equals await user,
-
hit tab to auto import that and pass in our validated data.
-
Once we have that user,
-
then we just need to create a new access token for them
-
and return that token back as a response for this request.
-
So we can just return user dot access tokens
-
dot create and pass in the user.
-
If you're using abilities here,
-
you can specify those as the second argument.
-
By default, this will look something like this
-
to allow all abilities.
-
And then we have additional options as the third argument.
-
By default, these access tokens will not expire.
-
So if you do want them to expire,
-
you can specify an expires in.
-
If you want it to be long live, but still have an expiration,
-
you might set this to something like five years,
-
which I believe is the default duration
-
for the remember me tokens for session authentication,
-
or you could set it to something like 30 days as well,
-
whatever your heart desires there.
-
For us, we're just gonna roll with the defaults.
-
So we'll take both of those off
-
and leave it at just create.
-
Now, if you're wondering where this access tokens comes from,
-
if we take a look at our user model,
-
we do have this with Authfinder,
-
the exact same that we have within session authentication
-
that we can use to check whether or not the user
-
has provided a correct email and password
-
that'll come into play whenever we cover login.
-
But if we scroll down a little bit further here,
-
we're gonna see a static access tokens property
-
that comes from dbAccessTokensProvider for the model,
-
and it provides in our user.
-
This access tokens provider is what provides
-
the actual access token property onto our user,
-
and it's a static property as well.
-
So if we take a look at what we have available on that,
-
so user.accessTokens here,
-
we can get all of the user's tokens.
-
We can create a token, delete a token,
-
find the specific token,
-
and verify the validity of a token as well.
-
So via that API,
-
working with tokens within AdonisJS
Join The Discussion! (11 Comments)
Please sign in or sign up for free to join in on the dicussion.
guy-aloni
Thank you very much for the clear and detailed explanation!
I wonder whether should be a good idea to add token expiration protection in front-end level, or rely on 409 server response in order to delete the token.
Please sign in or sign up for free to reply
tomgobich
The expiry time should be included with the initial token, so you could store it as well on your client and check it every once in a while to see if time is running up. I would rely on the server though for actually discerning whether it's expired or not.
Please sign in or sign up for free to reply
shahriar
Awesome Tutorial! Thank you.
Just one thing: I get this error:
```
Property 'currentAccessToken' does not exist on type 'never'
```
Please sign in or sign up for free to reply
tomgobich
Thanks Shahriar!
I would say to try restarting your text editor, sometimes it fails to pick up type changes. Apart from that, make sure your auth is configured correctly and double-check to make sure
accessTokens
is on your user model:If you're still having issues, please share a link to the repo or a repoduction and I can see if anything stands out!
Please sign in or sign up for free to reply
guy-aloni
Another question - how do you combine an access-token authentication with social authentication?
https://docs.adonisjs.com/guides/authentication/social-authentication
Please sign in or sign up for free to reply
tomgobich
Social Auth with AdonisJS forms the connection between AdonisJS and the 3rd party service and gives you the tools to discern who a user is. You can then use that info to create a user inside your application. From there, auth is done normally inside AdonisJS.
Please sign in or sign up for free to reply
nnakwe-anslem
Hi. Thank you for the detailed explanation.
I have been having trouble returning a value for the token after login. I keep getting the error message "Cannot save access token. The result '[0]' of insert query is unexpected" even though the token gets saved to the database. I'm not sure what I could be doing wrong.
You can check out the login method here:
https://github.com/ansman58/properties-backend/blob/main/app/controllers/auth.ts
Please sign in or sign up for free to reply
tomgobich
Hi Nnakwe!
Thank you for providing a repo! My best guess, after an initial look, is that you're probably running into an error at line 176 here:
https://github.com/adonisjs/auth/blob/develop/modules/access_tokens_guard/token_providers/db.ts#L176
I don't have MySQL installed currently to be able to test it out though. When I get some free time I can give it a go. You may want to try debugging
result
at line 175 to see exactly what you're getting back. Line 175 is where it's inserting your token and returning back the inserted id. Since you said it's inserting okay, my best guess is thereturning('id')
isn't returning as expected for MySQL or your specific MySQL version.Could potentially be
Something specific to MySQL/MySQL2
The specific MySQL version you have installed on your machine
A bug in KnexJS
A bug in AdonisJS at line 176.
Please sign in or sign up for free to reply
nnakwe-anslem
Thanks for pointing me to the source code. I have taken a look and the problem seems to be with the `returning('id')`, as you correctly pointed out. Is there any configuration I need to apply to MySQL in my codebase to enable the
returning
clause? I don't think it is fully supported in MySQL.EDIT: Switched to postgres and it now works fine. It's A bug in AdonisJS at line 176 when using MySQL.
Please sign in or sign up for free to reply
tomgobich
Looks like there's a previously reported issue with the same, I didn't look though the closed issues last time I checked so I missed it:
https://github.com/adonisjs/auth/issues/241
Sounds like it could potentially be related to how the UUID is defined as the issue creator reported switching that resolved his issue.
If you'd prefer using MySQL, it may be worth looking into and, if it is related, potentially providing your reproduction repository within that issue.
Please sign in or sign up for free to reply
ramiro-francisco-nzau
How to implement access token authentication with multiple models in Adonis 6? I've searched everywhere, and I can't make it work.
Please sign in or sign up for free to reply