Auth for API in 5 minutes via Symfony 6


Photo by FLY:D on Unsplash
Photo by FLY:D on Unsplash

Sometimes a developer needs to develop some minimal functionality as soon as possible, separately from the main application, because it’s faster and there is no all this bureaucracy with code review releases, approvals and months of testing.

To do this, the developer creates a new repository and implements his “miracle” there. It can be either a stand-alone tool that calculates the percentage of overtime pay increases depending on the rate and length of service, or a more complex system with access to a corporate database.

In any case, it would be irresponsible to leave any internal-only service available to the public. After all, we never know in advance how and for what purpose we can become a target for a potential agent “Smitt”.


The simplest ways to protect are:

  • deploy the application inside a private network, accessible only from a VPN.

  • Basic Auth.

  • authentication token.

  • Normal auth with registration & login.

  • Corporate SSO (like google auth).

Each of these methods has its pros and cons, depending on what situation you are currently in.

I will not describe in this article the pros and cons of each. Let’s assume that we settled on the option with authorization by token, for the following reasons:

  • We want access to employees who are not under the VPN.

  • We do not have SSO (and if we would, then we are too lazy to attach it to a small service written on the knee).

  • It makes no sense to do a full registration because there are no resources for moderation.


In total, we decided that we will use authorization based on a token.

Finally, let’s move on to coding.

In our case PHP 8.1 & symfony 6 & doctrine & security will be used.

Also, for convenience, the maker bundle will also be used.

0. To create a working environment, it is convenient to use Docker. You can use the blank to create a full-fledged local environment –Link.

  1. Installing the framework.

Full instructions on the site of Symfony itself https://symfony.com/doc/current/setup.html

  1. Install dependencies: security, doctrine, maker(dev).

composer require doctrine

composer require symfony/security-bundle

composer require symfony/maker-bundle — dev

  1. We create a user model.

./bin/console make:user
The name of the security user class (e.g. User) [User]:
 > User
Do you want to store user data in the database (via Doctrine)? (yes/no) [yes]:
 > yes
Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid) [email]:
 > username
Will this app need to hash/check user passwords? Choose No if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).
Does this app need to hash/check user passwords? (yes/no) [yes]:
 > no
 created: src/Entity/User.php
 created: src/Repository/UserRepository.php
 updated: config/packages/security.yaml
Success!

4. Let’s create and execute the migration so that the user’s table appears in the Database:

./bin/console make:migration./bin/console doctrine:migrations:migrate

Now the following 2 tables should appear in our database:

5. We make the necessary changes in the configuration to be able to log in using a token. You can select any field from your user table, you don’t have to specify exactly –token.

// config/packages/security.yaml
security:
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
        App\Entity\User:
            algorithm: auto
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: token
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            stateless: true
            provider: app_user_provider
            entry_point: App\Security\AuthenticationEntryPoint
            access_denied_handler: App\Security\AccessDeniedHandler
            custom_authenticators:
                - App\Security\ApiKeyAuthenticator
    access_control:
        - { path: ^/public, roles: PUBLIC_ACCESS }
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

6. Now let’s create the necessary classes in the Security directory:

The authorization key will be read from the request headers, but if you need a different implementation (for example, from GET parameters), you can easily change this in the method authenticate . The name of the header in which the auth token is expected is stored in a constant −HEADER_AUTH_TOKEN.

Next, we create a class that is responsible for generating a response in case the authorization is not passed.

The last class is responsible for generating responses if the user is authorized, but he does not have enough rights to perform the requested action.

7. Next, we have to test the operation of our configuration. To do this, we will create a controller with several methods.

8. Before moving on to testing, you need to create a user. Since this article is only about authorization, the author will allow himself to create a user with a simple sql query:

INSERT INTO demo.user (id, username, roles, token) VALUES (1, 'tester', '["ROLE_USER"]', 'super-secure-token');

9. Done! Let’s test the operation of the service (I prefer –Postman):


That’s all, now your application is protected, at least from crawlers, as well as from prying eyes and novice hackers from the support and accounting department $). You can also issue unique access keys to different groups of employees or even to each individual and, if desired, configure access levels based on the user’s role.

PS: In this article, the token is stored in the database in an unencrypted form, but this approach is not recommended, because User data may be at risk, you can read more details here.

I will be happy to answer questions, as well as write on topics of interest to you regarding web development.

Repository link –Link.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *