Adonis.js: Returning the user to the previously requested URL after authentication
I’m currently working on an Adonis.js codebase, and one of the things that had been mildly annoying me was the fact that after login, I was not taken back to the page I’d originally requested. Instead I was always taken to the same page, in this example /admin
.
Asking in the Adonis.js project discord, I received a hint that I could modify my AuthMiddleware
to add extra logic for handling this. Initially I’d totally missed that there even was an AuthMiddleware
in my project, it’s located in the app/middleware/auth_middleware.ts
file.
Once I had that small tip, it was quite easy to change to the AuthMiddleware
to store the requested URL, including the query string, in the session:
--- a/webapp/app/middleware/auth_middleware.ts
+++ b/webapp/app/middleware/auth_middleware.ts
@@ -1,6 +1,9 @@
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
import type { Authenticators } from '@adonisjs/auth/types'
+import { errors } from '@adonisjs/auth'
+
+export const returnToKey = 'return_to'
/**
* Auth middleware is used authenticate HTTP requests and deny
@@ -19,7 +22,15 @@ export default class AuthMiddleware {
guards?: (keyof Authenticators)[]
} = {}
) {
- await ctx.auth.authenticateUsing(options.guards, {
- loginRoute: this.redirectTo
- })
+ try {
+ await ctx.auth.authenticateUsing(options.guards, {
+ loginRoute: this.redirectTo
+ })
+ } catch (err) {
+ if (err instanceof errors.E_UNAUTHORIZED_ACCESS) {
+ // Place the current URL including the query string in the Session:
+ ctx.session.put(returnToKey, ctx.request.url(true))
+ }
+
+ throw err
+ }
return next()
}
}
The export of returnToKey
is so that we can later use the same session key with ease, because if you accidentally get the session key to be different between the two files, you’ll be left scratching your head (I accidentally did this whilst implementing).
You can also catch other errors here, such as errors.E_INVALID_CREDENTIALS
which is potentially thrown when you’re using the withAuthFinder
mixin on your User model and performing authentication in the way that’s described in documentation.
In the application I’m working on, we’re not using the withAuthFinder
method for authentication, and are instead using an a third-party provider via an OAuth/OIDC redirect based flow.
In our SessionController
, I just needed to change our fixed URL redirect once authentication had completed successfully from the following:
// In our login handler:
await auth.use('web').login(user)
response.redirect().toPath("/admin")
(where user
is an instance of the model we configured our auth guard with)
To the following:
// Add the import:
import { returnToKey } from '#middleware/auth_middleware'
// In our login handler:
const returnTo = session.pull(returnToKey, '/admin')
// Wipe the session to prevent session fixation (this
// is more necessary for OAuth based flows):
session.regenerate()
// find the user
const user = // ...
await auth.use('web').login(user)
response.redirect().toPath(returnTo)
This then gives us an authentication flow that takes us back to the previously requested URL after login.
Security Note #1: If you have open redirects in your application, you should absolutely filter the returnTo
URL to exclude those endpoints, otherwise you can be susceptible to phishing and other attacks.
Security Note #2: If you’re doing a custom login flow with additional session parameters, you should always call session.regenerate
to prevent session fixation attacks