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

Show last authors
1 {{box cssClass="floatinginfobox" title="**Contents**"}}
2 {{toc/}}
3 {{/box}}
4
5 {{info}}
6 The configuration has undergone significant changes and is not backward compatible with version 6.
7 {{/info}}
8
9 (% class="wikigeneratedid" %)
10 All bundle settings are located in the file {{code language="none"}}config/packages/ufo_json_rpc.yaml{{/code}}.
11
12 (% class="wikigeneratedid" %)
13 It is possible to configure the API security parameters and some data format parameters returned in the documentation request.
14
15 = Block {{code language="none"}}security{{/code}} =
16
17 Currently, the only mechanism for protecting access to your API is to set up an access key check (api_token).
18
19 == Parameters {{code language="none"}}protected_api{{/code}} and {{code language="none"}}protected_doc{{/code}} (boolean) ==
20
21 (% class="wikigeneratedid" %)
22 These parameters indicate whether the API methods and documentation should be protected, respectively.
23
24 (% class="wikigeneratedid" %)
25 By default, API methods are protected, and the documentation is open.
26
27 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
28 ufo_json_rpc:
29 security:
30 protected_api: true # Protection API requests
31 protected_doc: false # Protection API documentation
32 {{/code}}
33
34 (% id="cke_bm_164641S" style="display:none" %) (%%)If you protect your API through {{code language="none"}}protected_api{{/code}}, you need to configure the tokens that will provide access.
35
36 First, you need to decide on the token name.
37
38 == Parameter {{code language="none"}}token_name{{/code}} ==
39
40 The {{code language="none"}}RpcSecurity{{/code}} component will look for a specific key in the request headers that you can set in the package settings. The default value is {{code language="none"}}token_name: 'Ufo-RPC-Token'{{/code}}. You can set any other value that meets the following requirements.
41
42 {{spoiler title=" HTTP Header Naming Requirements"}}
43 The requirements for naming HTTP headers are not strictly regulated in terms of capitalization, as HTTP headers are case-insensitive. However, there are some general practices and standards that are usually followed for better readability and consistency:
44
45 - Capitalization: HTTP header names are typically written using CamelCase, where each word starts with a capital letter, e.g., Content-Type, User-Agent, Accept-Encoding. This does not affect the technical processing of headers but makes them easier to read.
46 - Uniqueness: Each header must have a unique name within the context of a single HTTP request or response. You cannot use the same name for different headers in the same request or response.
47 - Special Headers: There are headers used specifically for controlling cache behavior (Cache-Control), security (Strict-Transport-Security), authentication (Authorization), etc.
48 - RFC Norms: Header requirements are regulated by RFC documents that define Internet protocol standards. For example, common headers and their use are described in RFC 7231.
49 {{/spoiler}}
50
51
52
53
54
55 Parameter {{code language="none"}}clients_tokens{{/code}}
56
57 Next, specify an array of client tokens that will have access to the API.
58
59 There is some variability here.
60
61 === Tokens in parameters ===
62
63 (% class="box errormessage" %)
64 (((
65 **NOT RECOMMENDED!!!**
66 \\This approach is allowed only for local API testing
67 )))
68
69 You can hardcode tokens directly into the settings file.
70
71 This is bad from a security standpoint because if the code is stored in a public repository, anyone will have access to this token.
72
73 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
74 ufo_json_rpc:
75 security:
76 token_name: 'Ufo-RPC-Token'
77 clients_tokens:
78 - 'ClientTokenExample' # hardcoded token example. Importantly!!! Replace or delete it!
79 {{/code}}
80
81 === Tokens in environment variables ===
82
83 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.
84
85 In this case, you can specify tokens in environment variables (file {{code language="none"}}.env.local{{/code}} during local development). This mechanism is secure enough in terms of access storage.
86
87 (((
88 {{code language="ini" layout="LINENUMBERS" title=".env.local"}}
89 TOKEN_FOR_APP_1=9363074966579432364f8b73b3318f71
90 TOKEN_FOR_APP_2=456fg87g8h98jmnb8675r4445n8up365
91 {{/code}}
92
93 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
94 ufo_json_rpc:
95 security:
96 token_key_in_header: 'Ufo-RPC-Token'
97 clients_tokens:
98 - '%env(resolve:TOKEN_FOR_APP_1)%' # token example from .env.local
99 - '%env(resolve:TOKEN_FOR_APP_2)%' # token example from .env.local
100 {{/code}}
101 )))
102
103 === User Tokens ===
104
105 Suppose you need to create personal keys for your application users, perhaps you want to implement limits or other restrictions.
106 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.
107
108 To enable the JsonRpcServer to use your logic, you need to implement a custom class that implements the interface {{code language="none"}}Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator{{/code}}
109
110 {{code language="php" layout="LINENUMBERS" title="==== Example of a custom token validator ===="}}
111 <?php
112
113 namespace App\Services\RpcSecurity;
114
115 use App\Services\UserService;
116 use Symfony\Component\Security\Core\Exception\UserNotFoundException;
117 use Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator;
118 use Ufo\RpcError\RpcInvalidTokenException;
119
120 class UserTokenValidator implements ITokenValidator
121 {
122
123 public function __construct(protected UserService $userService) {}
124
125 public function isValid(string $token): true
126 {
127 try {
128 $this->userService->getUserByToken($token);
129 return true;
130 } catch (UserNotFoundException $e) {
131 throw new RpcInvalidTokenException(previous: $e);
132 }
133 }
134 }
135 {{/code}}
136
137 (% class="box warningmessage" %)
138 (((
139 **IMPORTANT!!!**
140 The {{code language="none"}}isValid{{/code}} method must return {{code language="none"}}true{{/code}} if the token exists and is valid, or throw {{code language="none"}}Ufo\RpcError\RpcInvalidTokenException{{/code}} otherwise.
141 )))
142
143 Next, you need to specify in the {{code language="none"}}config/services.yaml{{/code}} file that classes depending on the {{code language="none"}}ITokenValidator{{/code}} interface should accept your new class.
144
145 {{code language="yaml" layout="LINENUMBERS" title="config/services.yaml"}}
146 parameters:
147 # some parameters list
148 # ...
149
150 services:
151 # some services list
152 # ...
153
154 Ufo\JsonRpcBundle\Security\Interfaces\ITokenValidator:
155 class: App\Services\RpcSecurity\UserTokenValidator
156 {{/code}}
157
158 = Block {{code language="none"}}async{{/code}} =
159
160 This block is for configuring [[asynchronous transport>>doc:docs.JsonRpcBundle.functionality.async.WebHome]].
161
162 Add the parameter {{code language="none"}}rpc_async{{/code}}, which contains a DSN format string. This string is a configuration for [[Symfony Messenger>>https://symfony.com/doc/current/messenger.html]], indicating the asynchronous transport on which the RPC Server will await incoming requests if you have a consumer running ({{code language="none"}}php bin/console messenger:consume rpc_async{{/code}}). For a detailed understanding of this process, read the [[Symfony Messenger>>https://symfony.com/doc/current/messenger.html]] documentation.
163
164 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
165 ufo_json_rpc:
166 async:
167 rpc_async: '%env(resolve:RPC_TRANSPORT_DSN)%'
168
169 {{/code}}
170
171 (% class="box warningmessage" %)
172 (((
173 This configuration implies that you have an environment variable RPC_TRANSPORT_DSN set containing the DSN string.
174 )))
175
176 = Block {{code language="none"}}docs{{/code}} =
177
178 This block configures some parameters for generating documentation when you make a GET request to the RPC Server.
179
180 (% class="box infomessage" %)
181 (((
182 Starting from version 7, JsonRpcBundle generates API documentation that complies with the [[OpenRpc>>https://spec.open-rpc.org/]] specification.
183 )))
184
185 * {{code language="none"}}project_name{{/code}}: The project name displayed in the documentation
186 * {{code language="none"}}project_description{{/code}}: The project description
187 * {{code language="none"}}project_version{{/code}}: The current version of your API
188 * {{code language="none"}}async_dsn_info{{/code}}: Indicates whether to display information about asynchronous transport in the documentation
189 * {{code language="none"}}validations.symfony_asserts{{/code}}: <bool> Indicates whether to display the validation constraint string for the parameter (if you use [[validation>>doc:docs.JsonRpcBundle.add_rpc_service.assertions.WebHome]])
190
191 {{code language="yaml" layout="LINENUMBERS" title="config/packages/ufo_json_rpc.yaml"}}
192 ufo_json_rpc:
193 docs:
194 project_name: 'My Project'
195 project_description: 'My project description'
196 project_version: '1.0'
197 # Optional response details
198 async_dsn_info: false # Provide information about API that work asynchronously
199 validations:
200 symfony_asserts: false # Indicates if an array of Symfony validation constraints is used
201
202 {{/code}}
203
204 {{info}}
205 Do not worry about the security of your authorization data contained in the DSN.
206
207 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.
208
209 The template for protection is {{code language="none"}}/([\w\d_]*(?:secret|access|token|key)[_\w]*)=((?:\w|\d)+(?=&?))/{{/code}}.
210
211 Example:
212
213 {{code language="json" layout="LINENUMBERS" title="RPC_TRANSPORT_DSN=https://sqs.eu-west-3.amazonaws.com/123456789012/messages?access_key=AKIAIOSFODNN7EXAMPLE&secret_key=j17M97ffSVoKI0briFoo9a"}}
214 {
215 "async": {
216 "scheme": "https",
217 "host": "sqs.eu-west-3.amazonaws.com",
218 "path": "/123456789012/messages",
219 "query": "access_key={access_key}&secret_key={secret_key}"
220 }
221 }
222 {{/code}}
223 {{/info}}
224
225 === Documentation Example ===
226
227 {{code language="json" layout="LINENUMBERS" title="GET: /api"}}
228 {
229 "openrpc":"1.2.6",
230 "info":{
231 "title":"My Project",
232 "description":"My project description",
233 "contact":{
234 "name":"ufo-tech/json-rpc-bundle",
235 "url":"https://docs.ufo-tech.space/bin/view/docs/JsonRpcBundle/?language=en"
236 },
237 "license":{
238 "name":"MIT"
239 },
240 "version":"1.0"
241 },
242 "servers":[
243 {
244 "url":"https://mysite.com/api",
245 "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.",
246 "name":"UFO Json-RPC Server v.7.0.0",
247 "x-method":"POST",
248 "x-ufo":{
249 "envelop":"JSON-RPC-2.0/UFO-RPC-7.0.0",
250 "transport":{
251 "sync":{
252 "scheme":"https",
253 "host":"mysite.com",
254 "path":"/api",
255 "method":"POST"
256 },
257 "async":{
258 "scheme":"amqp",
259 "user":"{user}",
260 "pass":"{pass}",
261 "host":"mysite.com",
262 "port":5672,
263 "path":"/%2f/json-rpc"
264 }
265 },
266 "documentation":{
267 "json-rpc":"https://www.jsonrpc.org/specification",
268 "ufo-tech/json-rpc-bundle":"https://docs.ufo-tech.space/bin/view/docs/JsonRpcBundle/?language=en"
269 }
270 }
271 }
272 ],
273 "methods":[
274 {
275 "name":"getUserNameByUuid",
276 "tags":[
277 {
278 "name":"App\\Api\\UserApiService"
279 }
280 ],
281 "summary":"Get username by id",
282 "params":[
283 {
284 "name":"userId",
285 "description":"User Id format uuid",
286 "required":true,
287 "schema":{
288 "type":"string"
289 },
290 "x-ufo-assertions": "new Assert\\Uuid()"
291 }
292 ],
293 "result":{
294 "name":"string",
295 "description":"User Name",
296 "schema":{
297 "type":"string"
298 }
299 }
300 }
301 ]
302 }
303 {{/code}}