2. Bundle config

Last modified by Ashterix on 2024/07/11 11:49

The configuration has undergone significant changes and is not backward compatible with version 6.

All bundle settings are located in the file config/packages/ufo_json_rpc.yaml.

It is possible to configure the API security parameters and some data format parameters returned in the documentation request.

Block security

Currently, the only mechanism for protecting access to your API is to set up an access key check (api_token).

Parameters protected_api and protected_doc (boolean)

These parameters indicate whether the API methods and documentation should be protected, respectively.

By default, API methods are protected, and the documentation is open.

config/packages/ufo_json_rpc.yaml
1
2
3
4
ufo_json_rpc:
   security:
       protected_api: true     # Protection API requests
       protected_doc: false    # Protection API documentation

If you protect your API through protected_api, you need to configure the tokens that will provide access.

First, you need to decide on the token name.

Parameter token_name

The RpcSecurity component will look for a specific key in the request headers that you can set in the package settings. The default value is token_name: 'Ufo-RPC-Token'. You can set any other value that meets the following requirements.


HTTP Header Naming Requirements

Parameter clients_tokens

Next, specify an array of client tokens that will have access to the API.

There is some variability here.

Tokens in parameters

NOT RECOMMENDED!!!

This approach is allowed only for local API testing

You can hardcode tokens directly into the settings file.

This is bad from a security standpoint because if the code is stored in a public repository, anyone will have access to this token.

config/packages/ufo_json_rpc.yaml
1
2
3
4
5
ufo_json_rpc:
   security:
       token_name: 'Ufo-RPC-Token'
       clients_tokens:
            - 'ClientTokenExample'  # hardcoded token example. Importantly!!! Replace or delete it!

Tokens in environment variables

This is the most appropriate mechanism if you are developing a service for a distributed backend written in SOA (Service-Oriented Architecture). Typically, in this case, you need to open access to the API for one or a limited number of client applications, and key updates will not occur too often.

In this case, you can specify tokens in environment variables (file .env.local during local development). This mechanism is secure enough in terms of access storage.

.env.local
1
2
TOKEN_FOR_APP_1=9363074966579432364f8b73b3318f71
TOKEN_FOR_APP_2=456fg87g8h98jmnb8675r4445n8up365
config/packages/ufo_json_rpc.yaml
1
2
3
4
5
6
ufo_json_rpc:
   security:
       token_key_in_header: 'Ufo-RPC-Token'
       clients_tokens:
            - '%env(resolve:TOKEN_FOR_APP_1)%'   # token example from .env.local
            - '%env(resolve:TOKEN_FOR_APP_2)%'   # token example from .env.local

User Tokens

Suppose you need to create personal keys for your application users, perhaps you want to implement limits or other restrictions.
In this case, you do not need to specify the list of tokens in the configs; you can store them in a database or elsewhere according to your business logic. The only requirement is that you must have a service that can check whether the provided token exists.

To enable the JsonRpcServer to use your logic, you need to implement a custom class that implements the interface Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator

Example of a custom token validator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\Services\RpcSecurity;

use App\Services\UserService;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator;
use Ufo\RpcError\RpcInvalidTokenException;

class UserTokenValidator implements ITokenValidator
{

   public function __construct(protected UserService $userService) {}

   public function isValid(string $token): true
   {
       try {
           $this->userService->getUserByToken($token);
           return true;
        } catch (UserNotFoundException $e) {
           throw new RpcInvalidTokenException(previous: $e);
        }
    }
}

IMPORTANT!!!
The isValid method must return true if the token exists and is valid, or throw Ufo\RpcError\RpcInvalidTokenException otherwise.

Next, you need to specify in the config/services.yaml file that classes depending on the ITokenValidator interface should accept your new class.

config/services.yaml
1
2
3
4
5
6
7
8
9
10
parameters:
  # some parameters list
  # ...

services:
  # some services list
  # ...

   Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator:
       class: App\Services\RpcSecurity\UserTokenValidator

Block async

This block is for configuring asynchronous transport.

Add the parameter rpc_async, which contains a DSN format string. This string is a configuration for Symfony Messenger, indicating the asynchronous transport on which the RPC Server will await incoming requests if you have a consumer running (php bin/console messenger:consume rpc_async). For a detailed understanding of this process, read the Symfony Messenger documentation.

config/packages/ufo_json_rpc.yaml
1
2
3
4
ufo_json_rpc:
   async:
       rpc_async: '%env(resolve:RPC_TRANSPORT_DSN)%'

This configuration implies that you have an environment variable RPC_TRANSPORT_DSN set containing the DSN string.

Block docs

This block configures some parameters for generating documentation when you make a GET request to the RPC Server.

Starting from version 7, JsonRpcBundle generates API documentation that complies with the OpenRpc specification.

  • project_name: The project name displayed in the documentation
  • project_description: The project description
  • project_version: The current version of your API
  • async_dsn_info: Indicates whether to display information about asynchronous transport in the documentation
  • validations.symfony_asserts: <bool> Indicates whether to display the validation constraint string for the parameter (if you use validation)
config/packages/ufo_json_rpc.yaml
1
2
3
4
5
6
7
8
9
10
ufo_json_rpc:
   docs:
       project_name: 'My Project'
       project_description: 'My project description'
       project_version: '1.0'
       # Optional response details
       async_dsn_info:  false          # Provide information about API that work asynchronously  
       validations:
           symfony_asserts: false      # Indicates if an array of Symfony validation constraints is used

Do not worry about the security of your authorization data contained in the DSN.

The documenter is designed in such a way that before displaying DSN information, it removes user and password data, as well as other secret data such as tokens, secret keys, etc.

The template for protection is /([\w\d_]*(?:secret|access|token|key)[_\w]*)=((?:\w|\d)+(?=&?))/.

Example:

RPC_TRANSPORT_DSN=https://sqs.eu-west-3.amazonaws.com/123456789012/messages?access_key=AKIAIOSFODNN7EXAMPLE&secret_key=j17M97ffSVoKI0briFoo9a
1
2
3
4
5
6
7
8
{
   "async": {
       "scheme": "https",
       "host": "sqs.eu-west-3.amazonaws.com",
       "path": "/123456789012/messages",
       "query": "access_key={access_key}&secret_key={secret_key}"
    }
}

Documentation Example

GET: /api
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
{
  "openrpc":"1.2.6",
  "info":{
     "title":"My Project",
     "description":"My project description",
     "contact":{
        "name":"ufo-tech/json-rpc-bundle",
        "url":"https://docs.ufo-tech.space/bin/view/docs/JsonRpcBundle/?language=en"
      },
     "license":{
        "name":"MIT"
      },
     "version":"1.0"
   },
  "servers":[
      {
        "url":"https://mysite.com/api",
        "description":"Json-RPC api server from UFO Tec\n\nUFO Tech, or Universal Flexible Open Technologies, is an initiative aimed at providing PHP developers with tools to create complex yet user-friendly solutions for modern web applications and service-oriented architectures.",
        "name":"UFO Json-RPC Server v.7.0.0",
        "x-method":"POST",
        "x-ufo":{
           "envelop":"JSON-RPC-2.0/UFO-RPC-7.0.0",
           "transport":{
              "sync":{
                 "scheme":"https",
                 "host":"mysite.com",
                 "path":"/api",
                 "method":"POST"
               },
              "async":{
                 "scheme":"amqp",
                 "user":"{user}",
                 "pass":"{pass}",
                 "host":"mysite.com",
                 "port":5672,
                 "path":"/%2f/json-rpc"
               }
            },
           "documentation":{
              "json-rpc":"https://www.jsonrpc.org/specification",
              "ufo-tech/json-rpc-bundle":"https://docs.ufo-tech.space/bin/view/docs/JsonRpcBundle/?language=en"
            }
         }
      }
   ],
  "methods":[
      {
        "name":"getUserNameByUuid",
        "tags":[
            {
              "name":"App\\Api\\UserApiService"
            }
         ],
        "summary":"Get username by id",
        "params":[
            {
              "name":"userId",
              "description":"User Id format uuid",
              "required":true,
              "schema":{
                 "type":"string"
               },
              "x-ufo-assertions": "new Assert\\Uuid()"
            }
         ],
        "result":{
           "name":"string",
           "description":"User Name",
           "schema":{
              "type":"string"
            }
         }
      }
   ]
}