Source: pixabay.com. Licensed under CC0 Creative Commons
What will I learn?
- How to create an authentication system for a RESTful API
Requirements
- UNIX/Linux based OS
- Apache2 server with PHP7 installed
- MySQL database
- Text editor of choice
- Existing Firebase project
- Composer
- Base project found here
Difficulty
- Intermediate
Tutorial contents
This tutorial is a fourth one in the Web Development series where we jump into details on how to develop applications with Symfony using a sample project capable of sending push notifications to mobile devices. In the previous article the process of creating a form for an entity with a field validation was presented along with guidelines on how to create a controller using a RESTful API design principle.
A sample project used with this tutorial can be found here. It is basically the code you will end up with after completing the previous tutorial. It is recommended to clone the aforementioned project from the given github repository.
What aspects of Symfony web development will be covered in this tutorial?
- The process of building an authentication mechanism that will allow for authorising a user within a RESTful API and will prevent from a unauthorised access to API endpoints. While creating an authentication system, a user entity class will be added, along with a user manager and a user provider services declarations. Also the process of configuring a firewall will be explained.
An example usage for the created functionalities will be shown at the end of this tutorial.
How to create an authentication system for a RESTful API?
In the previous tutorial, the server application was extended with a RESTful API web services capabilities, therefore allowing for interacting with device entities via HTTP protocol (creating, removing and viewing device resources). An important thing here is the fact that, as of now, API web services can be accessed by anyone who knows the API endpoints. This might not be a desired outcome.
An authentication system that will be implemented in this tutorial will allow for authorisation with an api key, which will be passed as a header value with a Request
type object. This key will then be used to find a corresponding user object, which, if it exists, will represent an authorised user.
A user entity class
Before a user can be authorised, setting up an entity class is required, so a user data can be stored in a database. The aforementioned entity will contain three properties: User::$id
, User::$name
and User::$apiKey
.
An abstraction layer
The Security Component bundled with Symfony provides a simple interface which can be used as a mean of abstraction. It does not, however contain declarations for setUsername(string $username)
and setApiKey(string $apiKey)
methods which will be necessary to set a username and an api key during a user creation process. Lets change that now.
Begin by creating an interface called UserInterface
, which will extend a base interface bundled with Symfony, inside a src/Ptrio/MessageBundle/Model
directory.
<?php
// src/Ptrio/MessageBundle/Model/UserInterface.php
namespace App\Ptrio\MessageBundle\Model;
use Symfony\Component\Security\Core\User\UserInterface as BaseUserInterface;
interface UserInterface extends BaseUserInterface
{
/**
* @param string $username
*/
public function setUsername(string $username);
/**
* @param string $apiKey
*/
public function setApiKey(string $apiKey);
}
A base user class will contain definitions on how to handle all methods declared in a user interface file.
Add a file named User.php
inside a src/Ptrio/MessageBundle/Model
directory.
<?php
// src/Ptrio/MessageBundle/Model/User.php
namespace App\Ptrio\MessageBundle\Model;
abstract class User implements UserInterface
{
/**
* @var int
*/
protected $id;
/**
* @var string
*/
protected $username;
/**
* @var string
*/
protected $apiKey;
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return string
*/
public function getUsername(): string
{
return $this->username;
}
/**
* @param string $username
*/
public function setUsername(string $username)
{
$this->username = $username;
}
/**
* @return string
*/
public function getApiKey(): string
{
return $this->apiKey;
}
/**
* @param string $apiKey
*/
public function setApiKey(string $apiKey)
{
$this->apiKey = $apiKey;
}
/**
* @return array
*/
public function getRoles(): array
{
return ['ROLE_USER'];
}
/**
* @return void
*/
public function getPassword(): void
{
}
/**
* @return void
*/
public function getSalt(): void
{
}
/**
* @return void
*/
public function eraseCredentials(): void
{
}
}
A User::getRoles()
method returns an array containing user roles. Since an authentication system will not allow for an advanced access control, the only element contained in the aforementioned array will be a role called ROLE_USER
.
User::getPassword()
and User::getSalt()
do not return any values, since a password based authorisation will not be implemented.
An ability to erase user credentials is also not needed, so the body of a User::eraseCredentials()
method will remain empty.
A concrete class
A user concrete class will contain ORM mapping information that will allow to create a user table representation.
Add a file called User.php
to a src/Ptrio/MessageBundle/Entity
directory.
<?php
// src/Ptrio/MessageBundle/Entity/User.php
namespace App\Ptrio\MessageBundle\Entity;
use App\Ptrio\MessageBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class User
* @package App\Ptrio\MessageBundle\Entity
*
* @ORM\Entity
*/
class User extends BaseUser
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string", unique=true)
* @Assert\NotBlank()
*/
protected $username;
/**
* @ORM\Column(type="string", unique=true)
* @Assert\NotBlank()
*/
protected $apiKey;
}
A Doctrine migrations script has to be executed to update the database schema.
$ php bin/console doctrine:migration:diff
$ php bin/console doctrine:migration:migrate
A user manager class
To enable user management a user manager service has to be added. The principle behind creating a manager service is similar to the one laid down in the previous Web Development with Symfony series tutorials. The whole process is fairly simple - it requires an interface, a base class and a concrete class.
An abstraction layer
Start by adding an interface which will host method declarations for a manager class.
<?php
// src/Ptrio/MessageBundle/Model/UserManagerInterface.php
namespace App\Ptrio\MessageBundle\Model;
interface UserManagerInterface
{
/**
* @return UserInterface
*/
public function createUser();
/**
* @param UserInterface $user
*/
public function updateUser(UserInterface $user);
/**
* @param UserInterface $user
*/
public function removeUser(UserInterface $user);
/**
* @param array $criteria
* @return null|UserInterface
*/
public function findUserBy(array $criteria): ?UserInterface;
/**
* @param string $username
* @return null|UserInterface
*/
public function findUserByUsername(string $username): ?UserInterface;
/**
* @param string $apiKey
* @return null|UserInterface
*/
public function findUserByApiKey(string $apiKey): ?UserInterface;
/**
* @return string
*/
public function getClass(): string;
}
A base UserManager
class should be created in the next step.
<?php
// src/Ptrio/MessageBundle/Model/UserManager.php
namespace App\Ptrio\MessageBundle\Model;
abstract class UserManager implements UserManagerInterface
{
/**
* {@inheritdoc}
*/
public function createUser()
{
$class = $this->getClass();
return new $class;
}
/**
* {@inheritdoc}
*/
public function findUserByUsername(string $username): ?UserInterface
{
return $this->findUserBy(['username' => $username]);
}
/**
* {@inheritdoc}
*/
public function findUserByApiKey(string $apiKey): ?UserInterface
{
return $this->findUserBy(['apiKey' => $apiKey]);
}
}
A concrete class
A concrete class file should be created in a src/Ptrio/MessageBundle/Doctrine
directory.
<?php
// src/Ptrio/MessageBundle/Doctrine/UserManager.php
namespace App\Ptrio\MessageBundle\Doctrine;
use App\Ptrio\MessageBundle\Model\UserManager as BaseUserManager;
use Doctrine\Common\Persistence\ObjectManager;
use App\Ptrio\MessageBundle\Model\UserInterface;
class UserManager extends BaseUserManager
{
/**
* @var ObjectManager
*/
private $objectManager;
/**
* @var \Doctrine\Common\Persistence\ObjectRepository
*/
private $repository;
/**
* @var string
*/
private $class;
/**
* UserManager constructor.
* @param ObjectManager $objectManager
* @param string $class
*/
public function __construct(
ObjectManager $objectManager,
string $class
)
{
$this->objectManager = $objectManager;
$this->repository = $objectManager->getRepository($class);
$metadata = $objectManager->getClassMetadata($class);
$this->class = $metadata->getName();
}
/**
* {@inheritdoc}
*/
public function updateUser(UserInterface $user)
{
$this->objectManager->persist($user);
$this->objectManager->flush();
}
/**
* {@inheritdoc}
*/
public function removeUser(UserInterface $user)
{
$this->objectManager->remove($user);
$this->objectManager->flush();
}
/**
* {@inheritdoc}
*/
public function findUserBy(array $criteria): ?UserInterface
{
/** @var UserInterface|null $user */
$user = $this->repository->findOneBy($criteria);
return $user;
}
/**
* {@inheritdoc}
*/
public function getClass(): string
{
return $this->class;
}
}
Service configuration
A definition for a ptrio_message.user_manager
should be added to a services.yaml
file.
# src/Ptrio/MessageBundle/Resources/config/services.yaml
ptrio_message.user_manager:
class: 'App\Ptrio\MessageBundle\Doctrine\UserManager'
arguments:
- '@doctrine.orm.entity_manager'
- 'App\Ptrio\MessageBundle\Entity\User'
A user provider service
A user provider service, as the name implies, is responsible for providing a user object to an authentication system. It will contain methods allowing for fetching a user entity object from a database by a given api key.
Before we can proceed to the next step, besides the Security Component, installing a module called SecurityBundle
is necessary, so we can take an advantage of firewall capabilities.
SecurityBundle
can be installed with Composer.
$ composer require symfony/security-bundle
An abstraction layer
The Security Component comes bundled with a base interface called UserProviderInterface
. In order for the code to be organised better, creating a method responsible for finding a user object by a given api key is recommended. This can be achieved by extending the aforementioned base interface and adding a UserProviderInterface::findUser(string $apiKey)
method declaration to its contents.
A UserProviderInterface.php
file should be created in a src/Ptrio/MessageBundle/Security/Core/User
directory.
<?php
// src/Ptrio/MessageBundle/Security/Core/User/UserProviderInterface.php
namespace App\Ptrio\MessageBundle\Security\Core\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface as BaseUserProviderInterface;
interface UserProviderInterface extends BaseUserProviderInterface
{
/**
* @param string $apiKey
* @return null|UserInterface
*/
public function findUser(string $apiKey): ?UserInterface;
}
A concrete class
A concrete class called ApiKeyUserProvider
will define how the methods declared in a UserProviderInterface
are supposed to be handled.
<?php
// src/Ptrio/MessageBundle/Security/Core/User/ApiKeyUserProvider.php
namespace App\Ptrio\MessageBundle\Security\Core\User;
use App\Ptrio\MessageBundle\Model\UserManagerInterface;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
class ApiKeyUserProvider implements UserProviderInterface
{
/**
* @var UserManagerInterface
*/
private $userManager;
/**
* EntityUserProvider constructor.
* @param UserManagerInterface $userManager
*/
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
/**
* {@inheritdoc}
*/
public function loadUserByUsername($username)
{
$user = $this->findUser($username);
if ($user instanceof UserInterface) {
return $user;
}
throw new UsernameNotFoundException('User cannot be found.');
}
/**
* {@inheritdoc}
*/
public function refreshUser(UserInterface $user)
{
if ($user instanceof UserInterface) {
return $this->loadUserByUsername($user->getUsername());
}
throw new UnsupportedUserException('Unsupported user instance.');
}
/**
* {@inheritdoc}
*/
public function supportsClass($class)
{
return $this->userManager->getClass() === $class;
}
/**
* {@inheritdoc}
*/
public function findUser(string $apiKey): ?UserInterface
{
return $this->userManager->findUserByApiKey($apiKey);
}
}
Let’s go over some of the most important aspects found in an example ApiKeyUserProvider
class presented above.
A user object is found with the use of a user manager object, which is passed as a constructor argument.
A ApiKeyUserProvider::loadUserByUsername($username)
method in responsible for loading a user object. In case an object was not found in a database, it raises a UsernameNotFoundException
exception with a corresponding message.
A ApiKeyUserProvider::refreshUser(UserInterface $user)
method refreshes a user object on every request that is made.
A ApiKeyUserProvider::supportsClass($class)
method indicates if a given class is supported by a user provider service.
Service configuration
Adding a ptrio_message.user_provider.api_key
service definition to a services.yaml
file is necessary to allow for dependency injection.
# src/Ptrio/MessageBundle/Resources/config/services.yaml
ptrio_message.user_provider.api_key:
class: 'App\Ptrio\MessageBundle\Security\Core\User\ApiKeyUserProvider'
arguments:
- '@ptrio_message.user_manager'
Now a provider ptrio_message.user_provider.api_key
service has to be registered in a config/packages/security.yaml
file, so an authentication mechanism can use it to fetch a user object.
# config/packages/security.yaml
security:
providers:
user_provider:
id: 'ptrio_message.user_provider.api_key'
An authenticator mechanism class
An authenticator mechanism class will contain logic responsible for getting an api key from a request header and then using that header’s value to find a user object with the use of a provider service. Next, if a user object is found, an access to resources will be granted, but if it is not the case, a Response
type object will be returned, indicating that a client is not allowed to access the resource.
An authenticator class will extend a base AbstractGuardAuthenticator
class which a part of the Security Guard Component, and thence creating an abstraction layer will not be required in this step.
The Security Guard Component can be installed with Composer.
$ composer require symfony/security-guard
A concrete class
Begin by adding a file called TokenAuthenticator.php
to a src/Ptrio/MessageBundle/Security/Guard
directory.
<?php
// src/Ptrio/MessageBundle/Security/Guard/TokenAuthenticator.php
namespace App\Ptrio\MessageBundle\Security\Guard;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
class TokenAuthenticator extends AbstractGuardAuthenticator
{
/**
* {@inheritdoc}
*/
public function getCredentials(Request $request)
{
return [
'token' => $request->headers->get('X-AUTH-TOKEN'),
];
}
/**
* {@inheritdoc}
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$apiKey = $credentials['token'];
if (null === $apiKey) {
return null;
}
return $userProvider->loadUserByUsername($apiKey);
}
/**
* {@inheritdoc}
*/
public function checkCredentials($credentials, UserInterface $user)
{
return true;
}
/**
* {@inheritdoc}
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return null;
}
/**
* {@inheritdoc}
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
return new Response($exception->getMessage(), Response::HTTP_FORBIDDEN);
}
/**
* {@inheritdoc}
*/
public function start(Request $request, AuthenticationException $authException = null)
{
return new Response('Authentication is required to access this resource.', Response::HTTP_UNAUTHORIZED);
}
/**
* {@inheritdoc}
*/
public function supports(Request $request)
{
return $request->headers->has('X-AUTH-TOKEN');
}
/**
* {@inheritdoc}
*/
public function supportsRememberMe()
{
return false;
}
}
Time to explain what is going on in the example above.
A TokenAuthenticator::getCredentials(Request $request)
method takes responsibility for getting a X-AUTH-TOKEN
header value from a Request
object and passing that value, in a $credentials
array, as the first argument of a TokenAuthenticator::getUser($credentials, UserProviderInterface $userProvider)
method.
Note: A
TokenAuthenticator::getCredentials(Request $request)
method is called each time a request is made.
A TokenAuthenticator::getUser($credentials, UserProviderInterface $userProvider)
method uses a $credentials
array to get a X-AUTH-TOKEN
header value which is then passed to a user provider service to find a user object.
A TokenAuthenticator::checkCredentials($credentials, UserInterface $user)
is only called when a TokenAuthenticator::getUser($credentials, UserProviderInterface $userProvider)
method returned a UserInterface
type object. Since our authentication system authorises users with an api key, adding extra logic is not necessary. A value returned by a method described here is equal to true
.
Whenever a TokenAuthenticator::checkCredentials($credentials, UserInterface $user)
method returns true
, a TokenAuthenticator::onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
method is called, which can perform additional operations to verify user credentials. In the example presented above, no additional logic is provided, since our authentication system does not use a user password for authorisation purposes. Returning a null
value indicates that a request should be continued.
A TokenAuthenticator::onAuthenticationFailure(Request $request, AuthenticationException $exception)
method is responsible for handling situations where an authorisation process has failed. An adequate Response
object is returned with a 403
HTTP code.
In case that there is no X-AUTH-TOKEN
header provided with a request, a TokenAuthenticator::start(Request $request, AuthenticationException $authException = null)
method is called. Similarly to a TokenAuthenticator::onAuthenticationFailure(Request $request, AuthenticationException $exception)
method, a Response
object is returned to a client, this time with a 401
HTTP code.
A TokenAuthenticator::supports(Request $request)
method, which is called each time a request is made, indicates if an authentication algorithm should be used to secure a resource.
Finally, a TokenAuthenticator::supportsRememberMe()
method indicates if a remember_me
functionality is supposed to be used. Since our API is stateless, enabling it will not be necessary.
Firewall configuration
If an authentication system is to be used to secure RESTful API resources, a firewall definition has to be added to a config/packages/security.yaml
file.
# config/packages/security.yaml
firewalls:
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- 'App\Ptrio\MessageBundle\Security\Guard\TokenAuthenticator'
As shown in the example above, the firewall is called api
.
A pattern
parameter allows to define a regular expression for routes that are supposed to be secured with our firewall. In this particular scenario, all requests made behind a /api
API endpoint will be secured.
Note: A
stateless
value should be set totrue
, since our RESTful API is not allowed to store a client state.
A token generator class
Since following a single responsibility principle is desired here, adding a separate service, responsible for an api key generation process, is recommended. This service will be used during a creation of a user entity object.
An abstraction layer
To avoid hard class coupling, let’s first create a TokenGeneratorInterface
interface inside a src/Ptrio/MessageBundle/Util
directory.
<?php
// src/Ptrio/MessageBundle/Util/TokenGeneratorInterface.php
namespace App\Ptrio\MessageBundle\Util;
interface TokenGeneratorInterface
{
/**
* @return string
*/
public function generateToken(): string;
}
Every concrete class that will implement the above interface, will also have to implement a generateToken()
method.
A concrete class
In the next step, add a file named TokenGenerator.php
to the same directory.
<?php
// src/Ptrio/MessageBundle/Util/TokenGenerator.php
namespace App\Ptrio\MessageBundle\Util;
class TokenGenerator implements TokenGeneratorInterface
{
/**
* {@inheritdoc}
*/
public function generateToken(): string
{
return rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
}
}
As shown in the implementation above, a TokenGenerator::generateToken()
uses a PHP random_bytes
function to generate a cryptographically secure string which is 32 characters long. This string is then encoded with a base64_encode
function. In the next step, any occurrences of a +/
character will be replaced with -_
with the help of a strtr
function. In the last step, =
characters are removed, if any were found.
Note: A token generation algorithm can easily be replaced with any other, desired algorithm.
Service configuration
Lastly, add a ptrio_message.util.token_generator
service definitions to a services.yaml
file.
# src/Ptrio/MessageBundle/Resources/config/services.yaml
ptrio_message.util.token_generator:
class: 'App\Ptrio\MessageBundle\Util\TokenGenerator'
Command classes
At this stage, there is still no interface capable of handling the processes of creating and removing user objects. This can be changed by adding AddUserCommand
and RemoveUserCommand
command classes.
AddUserCommand class
A command responsible for adding a new user object will accept a user name as a required command-line argument. A ptrio_message.util.token_generator
service will be used to generate an api key for a newly created user. Also a user manager service will be responsible for checking if a user with a given name exists, and if that is not the case, a new object will be stored in the database. An api key will be returned as a command output.
Add a file called AddUserCommand.php
to a src/Ptrio/MessageBundle/Command
directory.
<?php
// src/Ptrio/MessageBundle/Command/AddUserCommand.php
namespace App\Ptrio\MessageBundle\Command;
use App\Ptrio\MessageBundle\Model\UserManagerInterface;
use App\Ptrio\MessageBundle\Util\TokenGeneratorInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class AddUserCommand extends Command
{
/**
* @var string
*/
public static $defaultName = 'ptrio:message:add-user';
/**
* @var UserManagerInterface
*/
private $userManager;
/**
* @var TokenGeneratorInterface
*/
private $tokenGenerator;
/**
* AddUserCommand constructor.
* @param UserManagerInterface $userManager
* @param TokenGeneratorInterface $tokenGenerator
*/
public function __construct(
UserManagerInterface $userManager,
TokenGeneratorInterface $tokenGenerator
)
{
$this->userManager = $userManager;
$this->tokenGenerator = $tokenGenerator;
parent::__construct();
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setDefinition([
new InputArgument('username', InputArgument::REQUIRED),
]);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$username = $input->getArgument('username');
if ($user = $this->userManager->findUserByUsername($username)) {
$output->writeln('User with a given username already exists.');
} else {
$user = $this->userManager->createUser();
$user->setUsername($username);
$apiKey = $this->tokenGenerator->generateToken();
$user->setApiKey($apiKey);
$this->userManager->updateUser($user);
$output->writeln('User created successfully with the following api key: ' . $apiKey);
}
}
}
RemoveUserCommand class
A RemoveUserCommand
command will use a user manager service to remove users objects from the storage service.
<?php
// src/Ptrio/MessageBundle/Command/AddUserCommand.php
namespace App\Ptrio\MessageBundle\Command;
use App\Ptrio\MessageBundle\Model\UserManagerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class RemoveUserCommand extends Command
{
/**
* @var string
*/
public static $defaultName = 'ptrio:message:remove-user';
/**
* @var UserManagerInterface
*/
private $userManager;
/**
* RemoveUserCommand constructor.
* @param UserManagerInterface $userManager
*/
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
parent::__construct();
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setDefinition([
new InputArgument('username', InputArgument::REQUIRED),
]);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$username = $input->getArgument('username');
if ($user = $this->userManager->findUserByUsername($username)) {
$this->userManager->removeUser($user);
$output->writeln('User removed successfully.');
} else {
$output->writeln('User with a given username does not exist.');
}
}
}
Service configuration
As usual, definitions for command services have to be added to a src/Ptrio/MessageBundle/Resources/config/services.yaml
file.
# src/Ptrio/MessageBundle/Resources/config/services.yaml
ptrio_message.add_user_command:
class: 'App\Ptrio\MessageBundle\Command\AddUserCommand'
arguments:
- '@ptrio_message.user_manager'
- '@ptrio_message.util.token_generator'
tags:
- { name: console.command }
ptrio_message.remove_user_command:
class: 'App\Ptrio\MessageBundle\Command\RemoveUserCommand'
arguments:
- '@ptrio_message.user_manager'
tags:
- { name: console.command }
Examples
The functionalities created in this tutorial allow for creating a new user, removing it and interacting with a RESTful API secured with an authentication system. Let’s see them in action.
Adding a user
A new user can be created with a ptrio:message:add-user
command.
$ php bin/console ptrio:message:add-user piotr42
Once a user was created successfully, an output similar to the one shown below will be displayed.
User created successfully with the following api key: yJsLCNsSc_gOeTsMVWlhF1pBuLV-Ic3KBbS_WnEqbRw
Removing a user
A ptrio:message:remove-user
command can be used to remove a user by a given name.
$ php bin/console ptrio:message:remove-user piotr42
Once a user was removed successfully, an output similar to the one shown below will be displayed.
User removed successfully.
Testing an authentication system
Since our authentication system is up and running, let’s begin by trying to make a request without a X-AUTH-TOKEN
header.
$ curl http://localhost/api/v1/devices/iphone-piotr
An information indicating that an authorisation is required to access the resource should be returned.
Authentication is required to access this resource.
Now, let’s try to make a request with a non-existing api key.
$ curl -H 'X-AUTH-TOKEN: Non-existing-api-key' http://localhost/api/v1/devices/iphone-piotr
An example output is shown below.
User cannot be found for a given api key.
Finally we can try to make a request with an existing api key.
$ curl -H 'X-AUTH-TOKEN: MMYKUE63gCyFc9blOwcrdr0EP9ghb7yiWZ2lr4fRauM' http://localhost/api/v1/devices/iphone-piotr
If everything went well, we should be able to see a requested device details in a JSON format, which is a default one for our RESTful API services.
{"id":1,"name":"iphone-piotr","token":"d1KeQHgkIoo:APA91bGBG7vg9rfX0PFbtn..."
Curriculum
- Web Development with Symfony: An application capable of dispatching push notifications to mobile devices via FCM [part 3]
- Web Development with Symfony: An application capable of dispatching push notifications to mobile devices via FCM [part 2]
- Web Development with Symfony: An application capable of dispatching push notifications to mobile devices via FCM [part 1]
Posted on Utopian.io - Rewarding Open Source Contributors
Thank you for the contribution. It has been approved.
You can contact us on Discord.
[utopian-moderator]
Hey @piotr42 I am @utopian-io. I have just upvoted you!
Achievements
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x