Web Authentication¶
This guide will introduce you to session-based authentication—a method of authentication commonly used for protecting web (front-end) pages.
Concept¶
In Computer Science (especially web frameworks), the concept of Authentication means verifying the identity of a user. This is not to be confused with Authorization which verifies privileges to a given resource
Session-based authentication uses cookies to re-authenticate users with each request to your website. It performs this logic via a middleware that you add to your application or specific routes.
You are responsible for initially authenticating the user to your application (either manually or by using methods from the Stateless (API) section). Once you have authenticated the user once, the middleware will use cookies to re-authenticate the user on subsequent requests automatically.
Example¶
Let's take a look at a simple session-based authentication example.
Pre-requisites¶
In order to do session-based authentication, you must have a way to initially authenticate your user. In other words, you need a method for logging them in. The Stateless (API) section covers some of these methods, but it's entirely up to you.
You will also need to have sessions configured for your application. You can learn more about this in Vapor → Sessions. Usually this will require adding the SessionsMiddleware
and choosing a KeyedCache
.
config.prefer(MemoryKeyedCache.self, for: KeyedCache.self) var middlewares = MiddlewareConfig() middlewares.use(SessionsMiddleware.self) // ... services.register(middlewares)
Model¶
Once you are ready to enable session-based authentication, the first step is to conform your user model to SessionAuthenticatable
.
extension User: SessionAuthenticatable { }
The conformance is empty since all of the required methods have default implementations.
Middleware¶
Once your model is conformed, you can use it to create an AuthenticationSessionsMiddleware
.
// create auth sessions middleware for user let session = User.authSessionsMiddleware() // create a route group wrapped by this middleware let auth = router.grouped(session) // create new route in this route group auth.get("hello") { req -> String in // }
Create a route group wrapped by this middleware using the route grouping methods. Any routes you want to support session-based authentication should use this route group.
You can also apply this middleware globally to your application if you'd like.
Route¶
Inside of any route closure wrapped by the session auth middleware, we can access our authenticated model using the authenticated(_:)
methods.
let user = try req.requireAuthenticated(User.self) return "Hello, \(user.name)!"
Here we are using the method prefixed with require
to throw an error if the user was not succesfully authenticated.
If you visit this route now, you should see a message saying no user has been authenticated. Let's resolve this by creating a way for our user to login!
Note
Use GuardAuthenticationMiddleware
to protect routes that do not call requireAuthenticated(_:)
or otherwise require authentication.
Login¶
For the sake of this example, we will just log in a pre-defined user with a fixed ID.
auth.get("login") { req -> Future<String> in return User.find(1, on: req).map { user in guard let user = user else { throw Abort(.badRequest) } try req.authenticate(user) return "Logged in" } }
Remember that this login route must go through the AuthenticationSessionsMiddleware
. The middleware is what will detect that we have authenticated a user and later restore the authentication automatically.
Upon visiting /hello
, you should recieve an error message stating that you are not logged in. If you then visit /login
first, followed by /hello
you should see that you are now successfully logged in!
If you open the inspector, you should notice a new cookie named "vapor-session"
has been added to your browser.
Redirecting unauthenticated users to the Login page¶
To redirect unauthenticated users from protected routes to the login page, add the RedirectMiddleware
to your protected routes.
func boot(router Router) throws { let auth = User.authSessionsMiddleware() // The Authentication Middleware let redirect = RedirectMiddleware<User>(path: "login") // Create the redirect middleware providing the path to redirect to (the login page) if the user is not logged in let loginRoutes = router.grouped([auth]) // The Login page should not be protected loginRoutes.get("login", use: renderLogin) let protectedRoutes = router.grouped([redirect, auth]) // Add routes to this group if they should redirect an unauthenticated user to the loggin page protectedRoutes.get("protected", use: renderProtected) }