Definitions
User Authentication is loosely defined as identifying the user based on his credentials. In other words, it provides access control for systems, by checking to see if the user's credentials match those saved in a database, in a data authentication server, or anywhere else.
The credentials, in the other hand, could be anything: an identifier, password, pin numbers, certificate, fingerprint, retina, voice. I mean anything that differentiate a user from another.
You can get more information about the authentication, its types and factors on this wonderful article.
Solutions
Developers and frameworks provide different solutions and visions, simple and complex, to manage user identification.
Let see some examples:
Zend Framework 2
Zend framework provides a dedicated module zend-authentication, which includes adapters
for different authentication methods, and an AuthenticationService
class.
use Zend\Authentication\AuthenticationService;
// Instantiate the authentication service
$auth = new AuthenticationService();
// Instantiate a dummy authentication adapter
$authAdapter = new Adapter($email, $password);
// Attempt authentication, saving the result
$result = $auth->authenticate($authAdapter);
After a successful authentication attempt, subsequent requests can query the authentication service to determine if an identity is present, and, if so, retrieve it:
if ($auth->hasIdentity()) {
// Identity exists; get it
$identity = $auth->getIdentity();
}
Laravel 5
Laravel is a huge framework in fact, offering also different solutions and tools to handle authentication.
Laravel's authentication services could be accessed via the Auth
facade.
use Illuminate\Support\Facades\Auth;
if (Auth::attempt($credentials)) {
// Authentication passed
}
else {
// Authentication failed
}
The attempt
method accepts an array of key/value pairs as its first argument, and will return a boolean
if the user is found in the database using the values in the array.
The authenticated user can be retrieved again via the Auth
facade.
use Illuminate\Support\Facades\Auth;
// Get the currently authenticated user...
$user = Auth::user();
// Get the currently authenticated user's ID...
$id = Auth::id();
Symfony 4
The Symfony's solution is quite complicated. The security
module provides multiple classes and interfaces to handle authentication.
use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
// instances of Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface
$providers = array(...)
// Instantiate the authentication manager
$authenticationManager = new AuthenticationProviderManager($providers);
// create a token, containing the user's credentials
$unauthenticatedToken = new UsernamePasswordToken($username, $password, $providerKey);
// validate the given token, and return an authenticated token
try {
$authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);
} catch (AuthenticationException $exception) {
// authentication failed
}
After authentication, the User object of the current user can be accessed via the getUser()
from inside a controller, this will look like:
public function index()
{
$user = $this->getUser();
}
or via the Security
class, available from version 3.4:
use Symfony\Component\Security\Core\Security;
public function indexAction(Security $security)
{
$user = $security->getUser();
}
Django 2
Django uses, by default, sessions and middlewares to hook the authentication system into request objects.
from django.contrib.auth import authenticate
def my_view(request):
user = authenticate(username='john', password='secret')
if user is not None:
# A backend authenticated the credentials
else:
# No backend authenticated the credentials
The user, authenticated or not, is always accessible within the request
object:
if request.user.is_authenticated:
# Do something for authenticated users.
...
else:
# Do something for anonymous users.
...
Passport.js
Authenticating requests is as simple as calling passport.authenticate()
and specifying which strategy to employ. authenticate()
's function signature is a standard Connect middleware, which makes it convenient to use as route middleware in Express applications.
app.post('/login', passport.authenticate('strategy-name'), function (req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.redirect('/users/' + req.user.username);
});
Unfortunately, passport
can only be used in an express-like applications, since authenticate()
returns a middleware function. It doesn't provide any API to use independently.
Conclusion
In my opinion, the Django solution is the best one, because, unlike the others, it separates the authentication
from login
.
- the authenticate function checks the user credentials against each authentication backend, and returns a
User
object if the credentials are valid for a backend. - but the login function saves the user’s ID in the session, making it persistent for several requests.
Signing in users is application specific. But user identification is shared between apps and systems, only the backend
differs.
From the above, and other examples not cited here, I ended up with a simple, stupid authentication manager, that dispatches the user credentials to a list of handlers.
interface AuthManager {
/**
* Check the credentials and return the user
*/
attempt (credentials: object): Promise<User | undefined>
/**
* Register an authenticator
*/
use (name: string, handler: Authenticator): this
}
The manager is an implementation of chain of responsibility
design pattern, and internally uses authenticators with the following contract:
interface Authenticator {
/**
* Try to handle the credentials, or delegate to the next handler.
*/
process (credentials: object, next: () => any): Promise<User | undefined>
}
I'm still working on this implementation to make it clean and framework-agnostic.
You may take a look at the develop branch.
What do you think guys ?
Top comments (0)