Skip to content

DivanteLtd/pimcore-jwt-auth

Repository files navigation

JWT Auth

This bundle provides JWT (Json Web Token) authentication for your Pimcore API. It is based on lexik/jwt-authentication-bundle

It is compatible and tested with PHP 7 and Pimcore 6.

Table of Contents

Prerequisites

This module requires Pimcore 6 and openssl extension.

Installation

Create User class in Pimcore:

Screenshot

Register dependencies in app/AppKernel.php:

  public function registerBundlesToCollection(BundleCollection $collection)
    {
        // ...
        if (class_exists('Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle')) {
            $collection->addBundle(new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle());
        }
        if (class_exists('Nelmio\CorsBundle\NelmioCorsBundle')) {
            $collection->addBundle(new Nelmio\CorsBundle\NelmioCorsBundle());
        }
    }

Generate the SSH keys :

$ mkdir app/config/jwt
$ openssl genrsa -out app/config/jwt/private.pem -aes256 4096
$ openssl rsa -pubout -in app/config/jwt/private.pem -out config/jwt/public.pem

In case first openssl command forces you to input password use following to get the private key decrypted

$ openssl rsa -in app/config/jwt/private.pem -out config/jwt/private2.pem
$ mv app/config/jwt/private.pem config/jwt/private.pem-back
$ mv app/config/jwt/private2.pem config/jwt/private.pem

Configuration

Configure the SSH keys path in your app/config/lexik_jwt_authentication.yaml :

lexik_jwt_authentication:
    secret_key:       '%kernel.project_dir%/app/config/jwt/private.pem' # required for token creation
    public_key:       '%kernel.project_dir%/app/config/jwt/public.pem'  # required for token verification
    pass_phrase:      'your_secret_passphrase' # required for token creation, usage of an environment variable is recommended
    token_ttl:        3600

Configure CORS configuration in app/config/nelmio_cors.yml :

nelmio_cors:
  defaults:
    allow_credentials: false
    allow_origin: []
    allow_headers: []
    allow_methods: []
    expose_headers: []
    max_age: 0
    hosts: []
    origin_regex: false
    forced_allow_origin_value: ~
  paths:
    '^/api/':
      allow_origin: ['*']
      allow_headers: ['*']
      allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
      max_age: 3600

Configure your app/config/security.yml :

security:
    # ...
    
   providers:
       pimcore_user_provider:
         id: login_bundle.security.user_provider
     firewalls:
       login:
         pattern: ^/api/login
         stateless: true
         anonymous: true
         provider: pimcore_user_provider
         json_login:
           check_path:               /api/login
           success_handler:          lexik_jwt_authentication.handler.authentication_success
           failure_handler:          lexik_jwt_authentication.handler.authentication_failure
   
       api:
          pattern:   ^/api
          stateless: true
          provider: pimcore_user_provider
          guard:
            authenticators:
              - lexik_jwt_authentication.jwt_token_authenticator
   
     access_control:
       - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
       - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }

Configure yor app/config/config.yml:

imports:
    # ...
    - { resource: lexik_jwt_authentication.yml }
    - { resource: nelmio_cors.yml }

Configure your config/routing.yml :

api_login_check:
    path: /api/login

Usage

The first step is to authenticate the user using its credentials. A classical form_login on an anonymously accessible firewall will do perfect.

Just set the provided lexik_jwt_authentication.handler.authentication_success service as success handler to generate the token and send it as part of a json response body.

Store it (client side), the JWT is reusable until its ttl has expired (3600 seconds by default). Create in Pimcore panel an object of class User and fill its credentials.

Note: You can test getting the token with a simple curl command like this:

curl -X POST -H "Content-Type: application/json" http://localhost/api/login -d '{"username":"admin","password":"test"}'

If it works, you will receive something like this:

{
   "token" : "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9.eyJleHAiOjE0MzQ3Mjc1MzYsInVzZXJuYW1lIjoia29ybGVvbiIsImlhdCI6IjE0MzQ2NDExMzYifQ.nh0L_wuJy6ZKIQWh6OrW5hdLkviTs1_bau2GqYdDCB0Yqy_RplkFghsuqMpsFls8zKEErdX5TYCOR7muX0aQvQxGQ4mpBkvMDhJ4-pE4ct2obeMTr_s4X8nC00rBYPofrOONUOR4utbzvbd4d2xT_tj4TdR_0tsr91Y7VskCRFnoXAnNT-qQb7ci7HIBTbutb9zVStOFejrb4aLbr7Fl4byeIEYgp2Gd7gY"
}

Use the token

Simply pass the JWT on each request to the protected firewall, either as an authorization header or as a query parameter.

By default only the authorization header mode is enabled : Authorization: Bearer {token}

About token expiration

Each request after token expiration will result in a 401 response. Redo the authentication process to obtain a new token.

Maybe you want to use a refresh token to renew your JWT. In this case you can check JWTRefreshTokenBundle.

More details

For more details head to LexikJWTAuthenticationBundle

Standards & Code Quality

This module respects PSR-2 code quality rule and our own PHPCS and PHPMD rulesets.

About Authors

Divante-logo

We are a Software House from Europe, existing from 2008 and employing about 150 people. Our core competencies are built around Magento, Pimcore and bespoke software projects (we love Symfony3, Node.js, Angular, React, Vue.js). We specialize in sophisticated integration projects trying to connect hardcore IT with good product design and UX.

We work for Clients like INTERSPORT, ING, Odlo, Onderdelenwinkel and CDP, the company that produced The Witcher game. We develop two projects: Open Loyalty - an open source loyalty program and Vue.js Storefront.

We are part of the OEX Group which is listed on the Warsaw Stock Exchange. Our annual revenue has been growing at a minimum of about 30% year on year.

Visit our website Divante.co for more information.