{"id":333417,"date":"2022-05-20T15:00:27","date_gmt":"2022-05-20T15:00:27","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=333417"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=333417","title":{"rendered":"<span>\u041a\u0430\u043a \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u0413\u043e\u0441\u0443\u0441\u043b\u0443\u0433\u0438 (\u0415\u0421\u0418\u0410) \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker \u0438 Typescript<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0412 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043f\u043e\u0441\u0442\u043e\u0432 \u0431\u043b\u043e\u0433\u0430 \u043c\u043e\u0439 \u043a\u043e\u043b\u043b\u0435\u0433\u0430 \u0418\u0432\u0430\u043d <a href=\"https:\/\/habr.com\/ru\/company\/waves_ent\/blog\/559358\/\"><u>\u043f\u0438\u0441\u0430\u043b<\/u><\/a> \u043e \u043d\u0430\u0448\u0435\u043c \u0431\u043b\u043e\u043a\u0447\u0435\u0439\u043d-\u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u0434\u043b\u044f \u043e\u043d\u043b\u0430\u0439\u043d-\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043d\u0438\u0439 WE.Vote. \u041e\u043d \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 WE.Vote \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439. \u041d\u043e \u0447\u0442\u043e\u0431\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043f\u0440\u0438\u043d\u044f\u0442\u0438\u044f \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u044e\u0440\u043b\u0438\u0446, \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0435\u0449\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0432\u0430\u0436\u043d\u043e\u0433\u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u2014 \u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u043d\u043e\u0439 \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432. \u0412 \u0420\u043e\u0441\u0441\u0438\u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0441\u0442\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 \u0415\u0421\u0418\u0410 (\u0415\u0434\u0438\u043d\u043e\u0439 \u0421\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438) \u2014 \u043f\u0440\u043e\u0449\u0435 \u0433\u043e\u0432\u043e\u0440\u044f, \u0441 \u0413\u043e\u0441\u0443\u0441\u043b\u0443\u0433\u0430\u043c\u0438. \u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u044d\u0442\u0430 \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 OAuth2-\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438, \u043a\u0430\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, Google \u0438\u043b\u0438 VK. \u0412 \u044d\u0442\u043e\u043c \u043f\u043e\u0441\u0442\u0435 \u043c\u044b \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u043f\u043e\u043c\u043e\u0447\u044c \u0442\u0435\u043c, \u043a\u0442\u043e \u0437\u0430\u0445\u043e\u0447\u0435\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0415\u0421\u0418\u0410 \u0432 \u0441\u0432\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0447\u0435\u0440\u0435\u0437 \u0441\u0442\u0435\u043a, \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0439 \u043d\u0430\u0448\u0435\u043c\u0443, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u0430\u0434\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u0441\u0441\u044b\u043b\u043e\u043a \u043f\u043e \u0415\u0421\u0418\u0410 \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/135\/abd\/12d\/135abd12d84f7903fe7b29797febf648.png\" width=\"780\" height=\"440\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/135\/abd\/12d\/135abd12d84f7903fe7b29797febf648.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>\u0417\u0430\u0447\u0435\u043c \u043d\u0430\u043c \u0415\u0421\u0418\u0410?<\/h3>\n<p>\u0421\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0424\u0435\u0434\u0435\u0440\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0437\u0430\u043a\u043e\u043d\u0443 <a href=\"https:\/\/its.1c.ru\/db\/newscomm\/content\/475113\/hdoc\"><u>\u2116 225-\u0424\u0417<\/u><\/a> \u043e\u0442 28.06.2021 \u00ab\u041e \u0432\u043d\u0435\u0441\u0435\u043d\u0438\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0432 \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0432\u0443\u044e \u0413\u0440\u0430\u0436\u0434\u0430\u043d\u0441\u043a\u043e\u0433\u043e \u043a\u043e\u0434\u0435\u043a\u0441\u0430 \u0420\u043e\u0441\u0441\u0438\u0439\u0441\u043a\u043e\u0439 \u0424\u0435\u0434\u0435\u0440\u0430\u0446\u0438\u0438\u00bb, \u043c\u043d\u043e\u0433\u0438\u0435 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0439 \u0432 \u0420\u0424 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u043f\u0440\u0430\u0432\u043e \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u0431\u0440\u0430\u043d\u0438\u044f \u0438 \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u043c \u0432\u043e\u043f\u0440\u043e\u0441\u0430\u043c \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u043e.<\/p>\n<p>\u0420\u0430\u043d\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0441 \u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u0438\u043b\u043e\u0439 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u0438 \u043e\u0447\u043d\u044b\u0445 \u0441\u043e\u0431\u0440\u0430\u043d\u0438\u0439 \u0438\u043b\u0438 \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043d\u0438\u0439 \u043f\u043e \u043f\u043e\u0447\u0442\u0435. \u0413\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e \u043f\u043e\u0447\u0442\u0435 \u043d\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u044c\u044e, \u0430 \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u0439 \u0441\u043e \u0432\u0441\u0435\u0439 \u0420\u043e\u0441\u0441\u0438\u0438 \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u2014\u00a0\u044d\u0442\u043e \u043a\u043e\u0448\u043c\u0430\u0440 \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0437\u0430\u0442\u0440\u0430\u0442.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u044f \u043f\u0440\u0438\u043d\u044f\u0442\u0438\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u043e \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u043d\u043e\u0432\u044b\u043c \u0444\u0435\u0434\u0435\u0440\u0430\u043b\u044c\u043d\u044b\u043c \u0437\u0430\u043a\u043e\u043d\u043e\u043c, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u043d\u043e\u0433\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043b\u0438\u0447\u043d\u043e\u0441\u0442\u0438 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432. \u0412 \u0420\u043e\u0441\u0441\u0438\u0438 \u044d\u0442\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0432\u0435\u0440\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u043c\u0443 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0443 \u043d\u0430 \u0413\u043e\u0441\u0443\u0441\u043b\u0443\u0433\u0430\u0445.<\/p>\n<h3>\u0421\u0442\u0435\u043a \u0438 \u0441\u0445\u0435\u043c\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438<\/h3>\n<p>\u0414\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c:<\/p>\n<ul>\n<li>\n<p>Typescript, ReactJS, NestJS<\/p>\n<\/li>\n<li>\n<p>\u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e CSP 4<\/p>\n<\/li>\n<li>\n<p>Docker, Kubernetes<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1f6\/598\/231\/1f6598231cdbb98404e555f1dae84ad9.png\" alt=\"\u0421\u0445\u0435\u043c\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438\" title=\"\u0421\u0445\u0435\u043c\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438\" width=\"1781\" height=\"1517\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1f6\/598\/231\/1f6598231cdbb98404e555f1dae84ad9.png\"\/><figcaption>\u0421\u0445\u0435\u043c\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438<\/figcaption><\/figure>\n<h3>\u0424\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0434\u043f\u0438\u0441\u0438<\/h3>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443, \u043a\u043e\u0435 \u043e \u0447\u0435\u043c \u0441\u0442\u043e\u0438\u0442 \u043f\u043e\u0434\u0443\u043c\u0430\u0442\u044c \u0437\u0430\u0440\u0430\u043d\u0435\u0435. \u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0434\u0440\u0443\u0433\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0439, \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u0415\u0421\u0418\u0410 \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u044c\u044e \u0413\u041e\u0421\u0422 \u0420 34.10\/11-2012, \u0430 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e API key. \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u043f\u043e\u0434\u043f\u0438\u0441\u044c \u043c\u043e\u0436\u043d\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0442\u0438\u043b\u0438\u0442\u044b <a href=\"https:\/\/cryptopro.ru\/products\/csp\"><u>\u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e CSP<\/u><\/a>. \u0414\u043b\u044f \u043d\u0430\u0441 \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0434\u0435\u0441\u044c \u2014 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c \u044d\u0442\u0443 \u0443\u0442\u0438\u043b\u0438\u0442\u0443 \u0432 Docker, \u0447\u0442\u043e\u0431\u044b \u0441 \u043d\u0435\u0439 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043a\u0430\u043a \u0441 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043d\u0430\u0448\u0435\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b. \u041f\u043e\u043b\u0443\u0447\u0438\u0432\u0448\u0438\u0439\u0441\u044f \u0441\u0435\u0440\u0432\u0438\u0441 \u043c\u044b \u0432\u044b\u043b\u043e\u0436\u0438\u043b\u0438 \u0432 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043d\u0430 <a href=\"https:\/\/github.com\/waves-enterprise\/cryptopro-sign\"><u>\u0433\u0438\u0442\u0445\u0430\u0431\u0435<\/u><\/a>. \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u043f\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0443 \u0435\u0441\u0442\u044c \u0432 README.md.<\/p>\n<p>\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u0431\u043e\u0440\u043a\u0438 Docker \u043e\u0431\u0440\u0430\u0437\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0441 \u0443\u0442\u0438\u043b\u0438\u0442\u043e\u0439 \u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e \u043c\u044b \u0432\u0441\u0442\u0440\u043e\u0438\u043b\u0438:<\/p>\n<ul>\n<li>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e \u0421SP 4 \u0438\u0437 .deb \u043f\u0430\u043a\u0435\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0438 \u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0433\u043e \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0439 \u0438\u043b\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u0440\u0435\u0434\u044b \u0415\u0421\u0418\u0410<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0441 PIN \u043a\u043e\u0434\u043e\u043c<\/p>\n<\/li>\n<li>\n<p>REST-\u0441\u0435\u0440\u0432\u0435\u0440 \u0441 \u043c\u0435\u0442\u043e\u0434\u043e\u043c, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043f\u0438\u0441\u0438<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0432\u0441\u044f \u043a\u0440\u0438\u043f\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044f \u0441\u043e\u0431\u0440\u0430\u043d\u0430 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0447\u0442\u043e-\u043d\u0438\u0431\u0443\u0434\u044c \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c. \u0412\u043e\u0442 \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435:<\/p>\n<pre><code class=\"javascript\">private async signParams(params: Record&lt;string, string>) {  const time = moment().format('YYYY.MM.DD HH:mm:ss ZZ')  const state = uuid()  const clientId = this.clientId  const scope = this.scope   const { data: { result: clientSecret } } = await axios.post&lt;{ result: string }>(    `${this.cryptoProServiceAddress}\/cryptopro\/sign`,    { text: [scope, time, clientId, state].join('') },  )   return {    ...params,    timestamp: time,    client_id: clientId,    scope,    state,    client_secret: clientSecret.replace(\/\\n\/g, ''),  } }<\/code><\/pre>\n<p>\u0421 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u043f\u043e\u0434\u043f\u0438\u0441\u0435\u0439 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c, \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c, \u043a\u0430\u043a \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0445\u0435\u043c\u0443 \u0432\u044b\u0448\u0435.<\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u0434\u043b\u044f \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0430 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0415\u0421\u0418\u0410<\/h3>\n<p>\u0412\u0441\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0440\u0435\u0448\u0430\u0435\u0442 \u043f\u0440\u043e\u0439\u0442\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u0415\u0421\u0418\u0410. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435 \u0441\u0441\u044b\u043b\u043a\u0443 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043d\u0430\u0448\u0435\u0433\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u0435\u0439.<\/p>\n<pre><code class=\"javascript\">async getAuthLink(redirectLink: string) {  const params = await this.signParams({    redirect_uri: redirectLink,    response_type: 'code',    access_type: 'offline',  })  const authQuery = new URLSearchParams(params)  const authURL = `${this.esiaHost}\/aas\/oauth2\/ac`  return `${authURL}?${authQuery}` }<\/code><\/pre>\n<p>\u0412 <strong>redirectLink<\/strong> \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0415\u0421\u0418\u0410 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0421\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0430 \u043d\u0435\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<h3>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0442\u043e\u043a\u0435\u043d\u0430 \u0415\u0421\u0418\u0410<\/h3>\n<h4>\u0417\u0430\u043f\u0440\u043e\u0441 \u0442\u043e\u043a\u0435\u043d\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438<\/h4>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0415\u0421\u0418\u0410 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u043d\u0430\u043c\u0438 \u0430\u0434\u0440\u0435\u0441\u0443. \u0415\u0421\u0418\u0410 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0432 \u0432\u0438\u0434\u0435 get-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 <strong>code<\/strong>. \u042d\u0442\u043e\u0442 \u0442\u043e\u043a\u0435\u043d \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434 \u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0441 \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<pre><code class=\"javascript\">async getTokens(code: string) {  try {    const params = await this.signParams({      grant_type: 'authorization_code',      token_type: 'Bearer',      redirect_uri: 'no',      code,    })    const authURL = `${this.host}\/aas\/oauth2\/te`    const authQuery = new URLSearchParams(params)    const { data: tokens } = await axios.post(`${authURL}?${authQuery}`)    return {      idToken: tokens.id_token,      accessToken: tokens.access_token,      refreshToken: tokens.refresh_token,    }  } catch (e) {    const status = e.response ? e.response.status : 500    const message = e.response ? e.response.data.error_description : e.message    throw new HttpException('Failed to get auth tokens: ' + message, status)  } }<\/code><\/pre>\n<h4>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435<\/h4>\n<p>\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u0433\u043e RSA \u043a\u043b\u044e\u0447\u0430 \u043e\u0442 \u0415\u0421\u0418\u0410 \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u0437 \u043d\u0435\u0433\u043e <strong>id<\/strong> \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u044d\u0442\u043e\u0433\u043e <strong>id<\/strong> \u0438 <strong>accessToken<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u0448\u0430\u0433\u0435, \u043c\u044b \u0443\u0436\u0435 \u043d\u0430\u043a\u043e\u043d\u0435\u0446 \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<pre><code class=\"javascript\">getUserIdFromToken(idToken: string) {  const decodedIdToken = verify(idToken, this.esiaPublicKey, {    algorithms: ['RS256'],    audience: 'WE_VOTE',  }) as EsiaParsedToken  return decodedIdToken['urn:esia:sbj']['urn:esia:sbj:oid'] }  async getUserInfo(tokens: EsiaTokens) {   const { idToken, accessToken } = tokens  const oId = this.getUserIdFromToken(idToken)   const [{ data: mainInfo }, { data: contactsInfo }] = await Promise.all([    axios.get(`${this.esiaHost}\/rs\/prns\/${oId}`, {      headers: {        Authorization: `Bearer ${accessToken}`,      },    }),    axios.get(`${this.esiaHost}\/rs\/prns\/${oId}\/ctts?embed=(elements)`, {      headers: {        Authorization: `Bearer ${accessToken}`,      },    }),  ])   const email = contactsInfo.elements.find(({ type }: { type: string }) => type === 'EML')   return {    id: oId,    firstName: mainInfo.firstName,    lastName: mainInfo.lastName,    surName: mainInfo.middleName,    trusted: mainInfo.trusted,    email: email ? {      value: email.value.toLowerCase(),      verified: email.vrfStu === 'VERIFIED',    } : null,  } }<\/code><\/pre>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0448\u0430\u0433\u0435 \u043c\u044b \u0443\u0436\u0435 \u0438\u043c\u0435\u0435\u043c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435. \u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043d\u0435\u0441\u0442\u0438 \u0438\u0445 \u0432 \u0441\u0432\u043e\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0438 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import { HttpException } from '@nestjs\/common' import * as moment from 'moment' import { v4 as uuid } from 'uuid' import { URLSearchParams } from 'url' import axios from 'axios' import { verify } from 'jsonwebtoken'  type EsiaTokens = {  idToken: string,  accessToken: string, }  type EsiaParsedToken = {  'urn:esia:sbj': {    'urn:esia:sbj:oid': string,  }, }  export class EsiaApiService {  private readonly clientId = 'WE_VOTE'  private readonly scope = ['openid', 'email', 'fullname'].join(' ')   constructor(    private readonly esiaHost: string, \/\/ 'https:\/\/esia-portal1.test.gosuslugi.ru' \u0438\u043b\u0438 'https:\/\/esia.gosuslugi.ru'    private readonly esiaPublicKey: string, \/\/ \u043c\u043e\u0436\u043d\u043e \u0432\u0437\u044f\u0442\u044c \u0438\u0437 http:\/\/esia.gosuslugi.ru\/public\/esia.zip    private readonly cryptoProServiceAddress: string, \/\/ \u0430\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043f\u043e\u0434\u043f\u0438\u0441\u0435\u0439 e.g 'http:\/\/127.0.0.1:3037'  ) {  }   async getAuthLink(redirectLink: string) {    const params = await this.signParams({      redirect_uri: redirectLink,      response_type: 'code',      access_type: 'offline',    })    const authQuery = new URLSearchParams(params)    const authURL = `${this.esiaHost}\/aas\/oauth2\/ac`    return `${authURL}?${authQuery}`  }   private async signParams(params: Record&lt;string, string>) {    const time = moment().format('YYYY.MM.DD HH:mm:ss ZZ')    const state = uuid()    const clientId = this.clientId    const scope = this.scope     const { data: { result: clientSecret } } = await axios.post&lt;{ result: string }>(      `${this.cryptoProServiceAddress}\/cryptopro\/sign`,      { text: [scope, time, clientId, state].join('') },    )     return {      ...params,      timestamp: time,      client_id: clientId,      scope,      state,      client_secret: clientSecret.replace(\/\\n\/g, ''),    }  }   async getTokens(code: string) {    try {      const params = await this.signParams({        grant_type: 'authorization_code',        token_type: 'Bearer',        redirect_uri: 'no',        code,      })      const authURL = `${this.esiaHost}\/aas\/oauth2\/te`      const authQuery = new URLSearchParams(params)      const { data: tokens } = await axios.post(`${authURL}?${authQuery}`)      return {        idToken: tokens.id_token,        accessToken: tokens.access_token,        refreshToken: tokens.refresh_token,      }    } catch (e) {      const status = e.response ? e.response.status : 500      const message = e.response ? e.response.data.error_description : e.message      throw new HttpException('Failed to get auth tokens: ' + message, status)    }  }   getUserIdFromToken(idToken: string) {    const decodedIdToken = verify(idToken, this.esiaPublicKey, {      algorithms: ['RS256'],      audience: this.clientId,    }) as EsiaParsedToken    return decodedIdToken['urn:esia:sbj']['urn:esia:sbj:oid']  }   async getUserInfo(tokens: EsiaTokens) {     const { idToken, accessToken } = tokens    const oId = this.getUserIdFromToken(idToken)     const [{ data: mainInfo }, { data: contactsInfo }] = await Promise.all([      axios.get(`${this.esiaHost}\/rs\/prns\/${oId}`, {        headers: {          Authorization: `Bearer ${accessToken}`,        },      }),      axios.get(`${this.esiaHost}\/rs\/prns\/${oId}\/ctts?embed=(elements)`, {        headers: {          Authorization: `Bearer ${accessToken}`,        },      }),    ])     const email = contactsInfo.elements.find(({ type }: { type: string }) => type === 'EML')     return {      id: oId,      firstName: mainInfo.firstName,      lastName: mainInfo.lastName,      surName: mainInfo.middleName,      trusted: mainInfo.trusted,      email: email ? {        value: email.value.toLowerCase(),        verified: email.vrfStu === 'VERIFIED',      } : null,    }  }  }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0441\u0442\u0430\u0442\u044c\u044f \u043e\u043a\u0430\u0437\u0430\u043b\u0430\u0441\u044c \u0434\u043b\u044f \u0432\u0430\u0441 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439. \u0416\u0435\u043b\u0430\u044e, \u0447\u0442\u043e\u0431\u044b \u0443 \u0432\u0430\u0441 \u0432\u0441\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u0431\u0435\u0437 \u043e\u0441\u043e\u0431\u044b\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c!\u00a0<\/p>\n<h3>\u041f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b \u043f\u043e \u0442\u0435\u043c\u0435<\/h3>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/waves-enterprise\/cryptopro-sign\"><u>\u041d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441 \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043f\u043e\u0434\u043f\u0438\u0441\u0435\u0439 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e<\/u><\/a>\u00a0<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/dbfun\/cryptopro\"><u>MVP-\u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u041a\u0440\u0438\u043f\u0442\u043e\u043f\u0440\u043e \u0432 \u0434\u043e\u043a\u0435\u0440\u0435<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/digital.gov.ru\/uploaded\/presentations\/esiametodicheskierekomendatsii223.pdf\"><u>\u041c\u0435\u0442\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u044f \u043f\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043e\u0442 \u0415\u0421\u0418\u0410<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/epp.genproc.gov.ru\/documents\/1323345\/0\/%D0%98%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%86%D0%B8%D1%8F+%D0%BF%D0%BE+%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B5+%D1%81+%D1%82%D0%B5%D1%81%D1%82%D0%BE%D0%B2%D0%BE%D0%B9+%D1%81%D1%80%D0%B5%D0%B4%D0%BE%D0%B9_1+4+%28%D0%95%D0%A1%D0%98%D0%90%29.pdf\/c143c953-148a-3abb-1257-885d8a1c79c7?t=1552474120296\"><u>\u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0439 \u0441\u0440\u0435\u0434\u043e\u0439 \u0415\u0421\u0418\u0410<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/cryptopro.ru\/products\/csp\"><u>\u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e CSP<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/company\/rosbank\/blog\/531230\/\"><u>\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043d\u0430 Java<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/www.openidentityplatform.org\/blog\/how-to-auth-via-esia\"><u>\u0412\u0442\u043e\u0440\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 Java<\/u><\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/miac.volmed.org.ru\/wiki\/index.php\/%D0%90%D0%B2%D1%82%D0%BE%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D0%B5%D0%B9_%D1%87%D0%B5%D1%80%D0%B5%D0%B7_OAUTH2_%D0%B2_%D0%95%D0%A1%D0%98%D0%90#.D0.A0.D0.B5.D0.B3.D0.B8.D1.81.D1.82.D1.80.D0.B0.D1.86.D0.B8.D1.8F_.D0.98.D0.A1\"><u>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 OAuth2-\u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438<\/u><\/a><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/waves_ent\/blog\/666894\/\"> https:\/\/habr.com\/ru\/company\/waves_ent\/blog\/666894\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0412 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043f\u043e\u0441\u0442\u043e\u0432 \u0431\u043b\u043e\u0433\u0430 \u043c\u043e\u0439 \u043a\u043e\u043b\u043b\u0435\u0433\u0430 \u0418\u0432\u0430\u043d <a href=\"https:\/\/habr.com\/ru\/company\/waves_ent\/blog\/559358\/\"><u>\u043f\u0438\u0441\u0430\u043b<\/u><\/a> \u043e \u043d\u0430\u0448\u0435\u043c \u0431\u043b\u043e\u043a\u0447\u0435\u0439\u043d-\u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u0434\u043b\u044f \u043e\u043d\u043b\u0430\u0439\u043d-\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043d\u0438\u0439 WE.Vote. \u041e\u043d \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 WE.Vote \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439. \u041d\u043e \u0447\u0442\u043e\u0431\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043f\u0440\u0438\u043d\u044f\u0442\u0438\u044f \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u044e\u0440\u043b\u0438\u0446, \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0435\u0449\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u0432\u0430\u0436\u043d\u043e\u0433\u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u2014 \u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u043d\u043e\u0439 \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432. \u0412 \u0420\u043e\u0441\u0441\u0438\u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0441\u0442\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0441 \u0415\u0421\u0418\u0410 (\u0415\u0434\u0438\u043d\u043e\u0439 \u0421\u0438\u0441\u0442\u0435\u043c\u043e\u0439 \u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438) \u2014 \u043f\u0440\u043e\u0449\u0435 \u0433\u043e\u0432\u043e\u0440\u044f, \u0441 \u0413\u043e\u0441\u0443\u0441\u043b\u0443\u0433\u0430\u043c\u0438. \u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u044d\u0442\u0430 \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 OAuth2-\u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438, \u043a\u0430\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, Google \u0438\u043b\u0438 VK. \u0412 \u044d\u0442\u043e\u043c \u043f\u043e\u0441\u0442\u0435 \u043c\u044b \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u043f\u043e\u043c\u043e\u0447\u044c \u0442\u0435\u043c, \u043a\u0442\u043e \u0437\u0430\u0445\u043e\u0447\u0435\u0442 \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0415\u0421\u0418\u0410 \u0432 \u0441\u0432\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0447\u0435\u0440\u0435\u0437 \u0441\u0442\u0435\u043a, \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0439 \u043d\u0430\u0448\u0435\u043c\u0443, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u0430\u0434\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u0441\u0441\u044b\u043b\u043e\u043a \u043f\u043e \u0415\u0421\u0418\u0410 \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h3>\u0417\u0430\u0447\u0435\u043c \u043d\u0430\u043c \u0415\u0421\u0418\u0410?<\/h3>\n<p>\u0421\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u0424\u0435\u0434\u0435\u0440\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0437\u0430\u043a\u043e\u043d\u0443 <a href=\"https:\/\/its.1c.ru\/db\/newscomm\/content\/475113\/hdoc\"><u>\u2116 225-\u0424\u0417<\/u><\/a> \u043e\u0442 28.06.2021 \u00ab\u041e \u0432\u043d\u0435\u0441\u0435\u043d\u0438\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0432 \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0432\u0443\u044e \u0413\u0440\u0430\u0436\u0434\u0430\u043d\u0441\u043a\u043e\u0433\u043e \u043a\u043e\u0434\u0435\u043a\u0441\u0430 \u0420\u043e\u0441\u0441\u0438\u0439\u0441\u043a\u043e\u0439 \u0424\u0435\u0434\u0435\u0440\u0430\u0446\u0438\u0438\u00bb, \u043c\u043d\u043e\u0433\u0438\u0435 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0439 \u0432 \u0420\u0424 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u043f\u0440\u0430\u0432\u043e \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u0431\u0440\u0430\u043d\u0438\u044f \u0438 \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u043c \u0432\u043e\u043f\u0440\u043e\u0441\u0430\u043c \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u043e.<\/p>\n<p>\u0420\u0430\u043d\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0441 \u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u0438\u043b\u043e\u0439 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u0438 \u043e\u0447\u043d\u044b\u0445 \u0441\u043e\u0431\u0440\u0430\u043d\u0438\u0439 \u0438\u043b\u0438 \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043d\u0438\u0439 \u043f\u043e \u043f\u043e\u0447\u0442\u0435. \u0413\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e \u043f\u043e\u0447\u0442\u0435 \u043d\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u044c\u044e, \u0430 \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u0439 \u0441\u043e \u0432\u0441\u0435\u0439 \u0420\u043e\u0441\u0441\u0438\u0438 \u0432 \u043e\u0434\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u2014\u00a0\u044d\u0442\u043e \u043a\u043e\u0448\u043c\u0430\u0440 \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0437\u0430\u0442\u0440\u0430\u0442.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u044f \u043f\u0440\u0438\u043d\u044f\u0442\u0438\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0434\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u043e \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u043d\u043e\u0432\u044b\u043c \u0444\u0435\u0434\u0435\u0440\u0430\u043b\u044c\u043d\u044b\u043c \u0437\u0430\u043a\u043e\u043d\u043e\u043c, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u043d\u043e\u0433\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043b\u0438\u0447\u043d\u043e\u0441\u0442\u0438 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432. \u0412 \u0420\u043e\u0441\u0441\u0438\u0438 \u044d\u0442\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0432\u0435\u0440\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u043c\u0443 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0443 \u043d\u0430 \u0413\u043e\u0441\u0443\u0441\u043b\u0443\u0433\u0430\u0445.<\/p>\n<h3>\u0421\u0442\u0435\u043a \u0438 \u0441\u0445\u0435\u043c\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438<\/h3>\n<p>\u0414\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c:<\/p>\n<ul>\n<li>\n<p>Typescript, ReactJS, NestJS<\/p>\n<\/li>\n<li>\n<p>\u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e CSP 4<\/p>\n<\/li>\n<li>\n<p>Docker, Kubernetes<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><figcaption>\u0421\u0445\u0435\u043c\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438<\/figcaption><\/figure>\n<h3>\u0424\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0434\u043f\u0438\u0441\u0438<\/h3>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443, \u043a\u043e\u0435 \u043e \u0447\u0435\u043c \u0441\u0442\u043e\u0438\u0442 \u043f\u043e\u0434\u0443\u043c\u0430\u0442\u044c \u0437\u0430\u0440\u0430\u043d\u0435\u0435. \u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0434\u0440\u0443\u0433\u0438\u0445 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0439, \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u0415\u0421\u0418\u0410 \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u044c\u044e \u0413\u041e\u0421\u0422 \u0420 34.10\/11-2012, \u0430 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e API key. \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u043f\u043e\u0434\u043f\u0438\u0441\u044c \u043c\u043e\u0436\u043d\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0443\u0442\u0438\u043b\u0438\u0442\u044b <a href=\"https:\/\/cryptopro.ru\/products\/csp\"><u>\u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e CSP<\/u><\/a>. \u0414\u043b\u044f \u043d\u0430\u0441 \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0434\u0435\u0441\u044c \u2014 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c \u044d\u0442\u0443 \u0443\u0442\u0438\u043b\u0438\u0442\u0443 \u0432 Docker, \u0447\u0442\u043e\u0431\u044b \u0441 \u043d\u0435\u0439 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043a\u0430\u043a \u0441 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u043c \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043d\u0430\u0448\u0435\u0439 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b. \u041f\u043e\u043b\u0443\u0447\u0438\u0432\u0448\u0438\u0439\u0441\u044f \u0441\u0435\u0440\u0432\u0438\u0441 \u043c\u044b \u0432\u044b\u043b\u043e\u0436\u0438\u043b\u0438 \u0432 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u043d\u0430 <a href=\"https:\/\/github.com\/waves-enterprise\/cryptopro-sign\"><u>\u0433\u0438\u0442\u0445\u0430\u0431\u0435<\/u><\/a>. \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u043f\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0443 \u0435\u0441\u0442\u044c \u0432 README.md.<\/p>\n<p>\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u0431\u043e\u0440\u043a\u0438 Docker \u043e\u0431\u0440\u0430\u0437\u0430 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0441 \u0443\u0442\u0438\u043b\u0438\u0442\u043e\u0439 \u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e \u043c\u044b \u0432\u0441\u0442\u0440\u043e\u0438\u043b\u0438:<\/p>\n<ul>\n<li>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0443\u0442\u0438\u043b\u0438\u0442\u044b \u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e \u0421SP 4 \u0438\u0437 .deb \u043f\u0430\u043a\u0435\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u043b\u0438\u0446\u0435\u043d\u0437\u0438\u0438 \u041a\u0440\u0438\u043f\u0442\u043e\u041f\u0440\u043e<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0433\u043e \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0439 \u0438\u043b\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u0440\u0435\u0434\u044b \u0415\u0421\u0418\u0410<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0441 PIN \u043a\u043e\u0434\u043e\u043c<\/p>\n<\/li>\n<li>\n<p>REST-\u0441\u0435\u0440\u0432\u0435\u0440 \u0441 \u043c\u0435\u0442\u043e\u0434\u043e\u043c, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0449\u0438\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043f\u0438\u0441\u0438<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0432\u0441\u044f \u043a\u0440\u0438\u043f\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044f \u0441\u043e\u0431\u0440\u0430\u043d\u0430 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0447\u0442\u043e-\u043d\u0438\u0431\u0443\u0434\u044c \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c. \u0412\u043e\u0442 \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435:<\/p>\n<pre><code class=\"javascript\">private async signParams(params: Record&lt;string, string>) {  const time = moment().format('YYYY.MM.DD HH:mm:ss ZZ')  const state = uuid()  const clientId = this.clientId  const scope = this.scope   const { data: { result: clientSecret } } = await axios.post&lt;{ result: string }>(    `${this.cryptoProServiceAddress}\/cryptopro\/sign`,    { text: [scope, time, clientId, state].join('') },  )   return {    ...params,    timestamp: time,    client_id: clientId,    scope,    state,    client_secret: clientSecret.replace(\/\\n\/g, ''),  } }<\/code><\/pre>\n<p>\u0421 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u043f\u043e\u0434\u043f\u0438\u0441\u0435\u0439 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c, \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c, \u043a\u0430\u043a \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0445\u0435\u043c\u0443 \u0432\u044b\u0448\u0435.<\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u0434\u043b\u044f \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0430 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0415\u0421\u0418\u0410<\/h3>\n<p>\u0412\u0441\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0440\u0435\u0448\u0430\u0435\u0442 \u043f\u0440\u043e\u0439\u0442\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u0415\u0421\u0418\u0410. \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435 \u0441\u0441\u044b\u043b\u043a\u0443 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043d\u0430\u0448\u0435\u0433\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u0435\u0439.<\/p>\n<pre><code class=\"javascript\">async getAuthLink(redirectLink: string) {  const params = await this.signParams({    redirect_uri: redirectLink,    response_type: 'code',    access_type: 'offline',  })  const authQuery = new URLSearchParams(params)  const authURL = `${this.esiaHost}\/aas\/oauth2\/ac`  return `${authURL}?${authQuery}` }<\/code><\/pre>\n<p>\u0412 <strong>redirectLink<\/strong> \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0430\u0434\u0440\u0435\u0441 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0415\u0421\u0418\u0410 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0421\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0441\u044b\u043b\u043a\u0443 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u0438 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0430 \u043d\u0435\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<h3>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u0442\u043e\u043a\u0435\u043d\u0430 \u0415\u0421\u0418\u0410<\/h3>\n<h4>\u0417\u0430\u043f\u0440\u043e\u0441 \u0442\u043e\u043a\u0435\u043d\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438<\/h4>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0415\u0421\u0418\u0410 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u043d\u0430\u043c\u0438 \u0430\u0434\u0440\u0435\u0441\u0443. \u0415\u0421\u0418\u0410 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0432 \u0432\u0438\u0434\u0435 get-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 <strong>code<\/strong>. \u042d\u0442\u043e\u0442 \u0442\u043e\u043a\u0435\u043d \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434 \u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0441 \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<pre><code class=\"javascript\">async getTokens(code: string) {  try {    const params = await this.signParams({      grant_type: 'authorization_code',      token_type: 'Bearer',      redirect_uri: 'no',      code,    })    const authURL = `${this.host}\/aas\/oauth2\/te`    const authQuery = new URLSearchParams(params)    const { data: tokens } = await axios.post(`${authURL}?${authQuery}`)    return {      idToken: tokens.id_token,      accessToken: tokens.access_token,      refreshToken: tokens.refresh_token,    }  } catch (e) {    const status = e.response ? e.response.status : 500    const message = e.response ? e.response.data.error_description : e.message    throw new HttpException('Failed to get auth tokens: ' + message, status)  } }<\/code><\/pre>\n<h4>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435<\/h4>\n<p>\u0418\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u043e\u0433\u043e RSA \u043a\u043b\u044e\u0447\u0430 \u043e\u0442 \u0415\u0421\u0418\u0410 \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u0437 \u043d\u0435\u0433\u043e <strong>id<\/strong> \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u044d\u0442\u043e\u0433\u043e <strong>id<\/strong> \u0438 <strong>accessToken<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u0448\u0430\u0433\u0435, \u043c\u044b \u0443\u0436\u0435 \u043d\u0430\u043a\u043e\u043d\u0435\u0446 \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<pre><code class=\"javascript\">getUserIdFromToken(idToken: string) {  const decodedIdToken = verify(idToken, this.esiaPublicKey, {    algorithms: ['RS256'],    audience: 'WE_VOTE',  }) as EsiaParsedToken  return decodedIdToken['urn:esia:sbj']['urn:esia:sbj:oid'] }  async getUserInfo(tokens: EsiaTokens) {   const { idToken, accessToken } = tokens  const oId = this.getUserIdFromToken(idToken)   const [{ data: mainInfo }, { data: contactsInfo }] = await Promise.all([    axios.get(`${this.esiaHost}\/rs\/prns\/${oId}`, {      headers: {        Authorization: `Bearer ${accessToken}`,      },    }),    axios.get(`${this.esiaHost}\/rs\/prns\/${oId}\/ctts?embed=(elements)`, {      headers: {        Authorization: `Bearer ${accessToken}`,      },    }),  ])   const email = contactsInfo.elements.find(({ type }: { type: string }) => type === 'EML')   return {    id: oId,    firstName: mainInfo.firstName,    lastName: mainInfo.lastName,    surName: mainInfo.middleName,    trusted: mainInfo.trusted,    email: email ? {      value: email.value.toLowerCase(),      verified: email.vrfStu === 'VERIFIED',    } : null,  } }<\/code><\/pre>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0448\u0430\u0433\u0435 \u043c\u044b \u0443\u0436\u0435 \u0438\u043c\u0435\u0435\u043c \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435. \u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043d\u0435\u0441\u0442\u0438 \u0438\u0445 \u0432 \u0441\u0432\u043e\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0438 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f \u043d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">import { HttpException } from '@nestjs\/common' import * as moment from 'moment' import { v4 as uuid } from 'uuid' import { URLSearchParams } from 'url' import axios from 'axios' import { verify } from 'jsonwebtoken'  type EsiaTokens = {  idToken: string,  accessToken: string, }  type EsiaParsedToken = {  'urn:esia:sbj': {    'urn:esia:sbj:oid': string,  }, }  export class EsiaApiService {  private readonly clientId = 'WE_VOTE'  private readonly scope = ['openid', 'email', 'fullname'].join(' ')   constructor(    private readonly esiaHost: string, \/\/ 'https:\/\/esia-portal1.test.gosuslugi.ru' \u0438\u043b\u0438 'https:\/\/esia.gosuslugi.ru'    private readonly esiaPublicKey: string, \/\/ \u043c\u043e\u0436\u043d\u043e \u0432\u0437\u044f\u0442\u044c \u0438\u0437 http:\/\/esia.gosuslugi.ru\/public\/esia.zip    private readonly cryptoProServiceAddress: string, \/\/ \u0430\u0434\u0440\u0435\u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043f\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043f\u043e\u0434\u043f\u0438\u0441\u0435\u0439 e.g 'http:\/\/127.0.0.1:3037'  ) {  }   async getAuthLink(redirectLink: string) {    const params = await this.signParams({      redirect_uri: redirectLink,      response_type: 'code',      access_type: 'offline',    })    const authQuery = new URLSearchParams(params)    const authURL = `${this.esiaHost}\/aas\/oauth2\/ac`    return `${authURL}?${authQuery}`  }   private async signParams(params: Record&lt;string, string>) {    const time = moment().format('YYYY.MM.DD HH:mm:ss ZZ')    const state = uuid()    const clientId = this.clientId    const scope = this.scope     const { data: { result: clientSecret } } = await axios.post&lt;{ result: string }>(      `${this.cryptoProServiceAddress}\/cryptopro\/sign`,      { text: [scope, time, clientId, state].join('') },    )     return {      ...params,      timestamp: time,      client_id: clientId,      scope,      state,      client_secret: clientSecret.replace(\/\\n\/g, ''),    }  }   async getTokens(code: string) {    try {      const params = await this.signParams({        grant_type: 'authorization_code',        token_type: 'Bearer',        redirect_uri: 'no',        code,      })      const authURL = `${this.esiaHost}\/aas\/oauth2\/te`      const authQuery = new URLSearchParams(params)      const { data: tokens } = await axios.post(`${authURL}?${authQuery}`)      return {        idToken: tokens.id_token,        accessToken: tokens.access_token,        refreshToken: tokens.refresh_token,      }    } catch (e) {      const status = e.response ? e.response.status : 500      const message = e.response ? e.response.data.error_description : e.message      throw new HttpException('Failed to get auth tokens: ' + message, status)    }  }   getUserIdFromToken(idToken: string) {    const decodedIdToken = verify(idToken, this.esiaPublicKey, {      algorithms: ['RS256'],      audience: this.clientId,    }) as EsiaParsedToken    return decodedIdToken['urn:esia:sbj']['urn:esia:sbj:oid']  }   async getUserInfo(tokens: EsiaTokens) {     const { idToken, accessToken } = tokens    const oId = this.getUserIdFromToken(idToken)     const [{ data: mainInfo }, { data: contactsInfo }] = await Promise.all([      axios.get(`${this.esiaHost}\/rs\/prns\/${oId}`, {        headers: {          Authorization: `Bearer ${accessToken}`,        },      }),      axios.get(`${this.esiaHost}\/rs\/prns\/${oId}\/ctts?embed=(elements)`, {        headers: {          Authorization: `Bearer ${accessToken}`,        },      }),    ])     const email =<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-333417","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/333417","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=333417"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/333417\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=333417"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=333417"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=333417"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}