Symfony API Rest login and register

6.4k Views Asked by At

I'm finishing developing a API rest with common endpoints using FOSUserBundle, FOSResBundle and FOSoAuthBundle.

In a parallel way, I'm developing another symfony app to consume the services but I'm having several doubts about how to register and login users via API endpoints. My idea is to have 2 controllers refered it's routes (such as /login and /register) and do this actions via api endpoint.

with FOSUserbundle this actions are simple but don't know how to 'consume' trough APIRest service.

1

There are 1 best solutions below

0
benoliver999 On

I know it's only half an answer but I currently do this with login, but not registration (I don't need it).

I am using Symfony 4 and flex, but it should work on 3.4 I think. I am also using json.

Bundles

Bundles I use for this (on top of fosuser etc):

  • jms/serializer-bundle
  • lexik/jwt-authentication-bundle
  • friendsofsymfony/rest-bundle

Config

Fos Rest

fos_rest:
    body_listener: true
    param_fetcher_listener: force
    format_listener:
        enabled: true
        rules:
            - { path: ^/, priorities: [ json ], fallback_format: json, prefer_extension: true }
    view:
        view_response_listener: 'force'
        formats:
            json: true
            xml: false
            rss: false
        mime_types:
            json: ['application/json', 'application/x-json']
    routing_loader:
        default_format:  json
        include_format:  false
    exception:
        enabled: true

Fos User

fos_user:
    db_driver: orm 
    firewall_name: api 
    user_class: App\Entity\User
    from_email:
        address: [email protected]
        sender_name: Admin

JMS Serializer

jms_serializer:
    visitors:
        xml:
            format_output: '%kernel.debug%'

Lexik JWT

You need you generate tokens and put their location in your .env file. More info and a guide here.

lexik_jwt_authentication:
    private_key_path: '%kernel.project_dir%/%env(JWT_PRIVATE_KEY_PATH)%'
    public_key_path:  '%kernel.project_dir%/%env(JWT_PUBLIC_KEY_PATH)%'
    pass_phrase:      '%env(JWT_PASSPHRASE)%'
    token_ttl:         3600 #whatever you like
    token_extractors:
        authorization_header: ~
        cookie: ~
        query_parameter: ~

Security

security:
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512 #probably use bcrypt
    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        api_login:
            pattern:  ^/login
            stateless: true
            anonymous: true
            form_login:
                check_path:               /login
                require_previous_session: false
                username_parameter:       username
                password_parameter:       password
                success_handler:          lexik_jwt_authentication.handler.authentication_success
                failure_handler:          lexik_jwt_authentication.handler.authentication_failure

        api:
            pattern:   ^/
            stateless: true
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator

    access_control:
        - { path: ^/,                 role: IS_AUTHENTICATED_FULLY }

Routes

login:
    type: rest
    resource: App\Controller\LoginController

Controller

I made a loginController that is really just a placeholder to override the route

<?php

namespace App\Controller;

use FOS\RestBundle\Controller\Annotations;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Routing\ClassResourceInterface;
use FOS\RestBundle\Controller\Annotations\RouteResource;

/**
 * @RouteResource("login", pluralize=false)
 */
class LoginController extends FOSRestController implements ClassResourceInterface
{
    public function postAction()
    {
        throw new \DomainException('You should never see this');
    }
}

Testing it

You can fire a POST request to /login and it should return a token!

{
  "username": "my_user",
  "password": "passw0rd"
}

Registration

I haven't actually done this but I have a simple enough idea...

Open a route to registration in your security.yaml

security:
    firewalls:
        api_register:
            pattern: ^/register
            anonymous: true

    access_control:
        - { path: ^/register$,        role: IS_AUTHENTICATED_ANONYMOUSLY }

Register the route

registration:
    type: rest
    resource: App\Controller\RegistrationController

Write the controller method

  1. Get the request data
  2. User the FosUserManager to create a new user - https://symfony.com/doc/current/bundles/FOSUserBundle/user_manager.html