{"id":443465,"date":"2024-12-26T08:56:00","date_gmt":"2024-12-26T08:56:00","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=443465"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=443465","title":{"rendered":"<span>\u0420\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e Convex. \u0427\u0430\u0441\u0442\u044c 2<\/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-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/lq\/qm\/e7\/lqqme7wqqxhte55gsiz94gwwh4u.png\" data-src=\"https:\/\/habrastorage.org\/webt\/lq\/qm\/e7\/lqqme7wqqxhte55gsiz94gwwh4u.png\"\/> <\/p>\n<p> \u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p> <\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0435\u0440\u0438\u0438 \u0441\u0442\u0430\u0442\u0435\u0439 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e \u043e <a href=\"https:\/\/www.convex.dev\/\">Convex<\/a> \u2014 \u043d\u043e\u0432\u043e\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u043c \u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0438 BaaS (Backend as a Service \u2014 \u0431\u044d\u043a\u0435\u043d\u0434 \u043a\u0430\u043a \u0443\u0441\u043b\u0443\u0433\u0430), \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e\u043e\u0431\u0435\u0449\u0430\u044e\u0449\u0435 \u0438 \u0431\u044b\u0441\u0442\u0440\u043e \u043d\u0430\u0431\u0438\u0440\u0430\u0435\u0442 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0441\u0442\u044c \u0441\u0440\u0435\u0434\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432.<\/p>\n<p> <\/p>\n<p>\u041d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0438\u0439 \u0434\u0435\u043d\u044c Convex \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u043c\u0435\u0448\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430, \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438\/\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0437\u0430\u0434\u0430\u0447 \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430.<\/p>\n<p> <\/p>\n<p>\u042d\u0442\u0430 \u0432\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0441\u0435\u0440\u0438\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p> <\/p>\n<ul>\n<li><a href=\"https:\/\/habr.com\/ru\/companies\/timeweb\/articles\/844878\/\">\u041f\u0435\u0440\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c<\/a><\/li>\n<\/ul>\n<p><a name=\"habracut\"><\/a> <\/p>\n<h1 id=\"-autentifikaciya\">\u276f \u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f<\/h1>\n<p> <\/p>\n<p>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0442\u044c \u0438\u043c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0434\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<p> <\/p>\n<p><strong>Convex Auth<\/strong><\/p>\n<p> <\/p>\n<p>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u043f\u0440\u044f\u043c\u043e \u0432 Convex \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 Convex Auth, \u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435. \u042d\u0442\u043e \u043b\u0435\u0433\u043a\u0438\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438\/\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0445 email\/\u0421\u041c\u0421 \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u0435\u0439.<\/p>\n<p> <\/p>\n<p><strong>\u0421\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0435 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438<\/strong><\/p>\n<p> <\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u044b \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e Convex \u0441\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u043c\u0438 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430\u043c\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u042d\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u043d\u043e \u044d\u0442\u0438 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u043f\u043e\u0432\u0435\u0440\u0445 \u043e\u0441\u043d\u043e\u0432 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438:<\/p>\n<p> <\/p>\n<ul>\n<li><a href=\"https:\/\/docs.convex.dev\/auth\/clerk\">Clerk<\/a> \u2014 \u0431\u043e\u043b\u0435\u0435 \u043d\u043e\u0432\u0430\u044f \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0441 \u043b\u0443\u0447\u0448\u0435\u0439 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 Next.js \u0438 React Native<\/li>\n<li><a href=\"https:\/\/docs.convex.dev\/auth\/auth0\">Auth0<\/a> \u2014 \u0431\u043e\u043b\u0435\u0435 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0430\u044f \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0441 \u043e\u0433\u0440\u043e\u043c\u043d\u044b\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439<\/li>\n<\/ul>\n<p> <\/p>\n<h2 id=\"-convex-auth\">\u276f Convex Auth<\/h2>\n<p> <\/p>\n<p>Convex Auth \u2014 \u044d\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u0440\u044f\u043c\u043e \u0432 Convex. \u041e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0431\u0435\u0437 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<p> <\/p>\n<ul>\n<li><a href=\"https:\/\/labs.convex.dev\/auth-example\">\u0414\u0435\u043c\u043e<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/get-convex\/convex-auth-example\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/a><\/li>\n<\/ul>\n<p> <\/p>\n<p><strong>\u041d\u0430\u0447\u0430\u043b\u043e \u0440\u0430\u0431\u043e\u0442\u044b<\/strong><\/p>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0441 Convex \u0438 Convex Auth \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443:<\/p>\n<p> <\/p>\n<pre><code class=\"bash\">npm create convex@latest<\/code><\/pre>\n<p> <\/p>\n<p>\u0438 \u0432\u044b\u0431\u0440\u0430\u0442\u044c <code>React (Vite)<\/code> \u0438 <code>Convex Auth<\/code>.<\/p>\n<p> <\/p>\n<p>\u041f\u0440\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 Convex Auth \u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<p> <\/p>\n<p><strong>\u041e\u0431\u0437\u043e\u0440<\/strong><\/p>\n<p> <\/p>\n<p>Convex Auth \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438:<\/p>\n<p> <\/p>\n<ol>\n<li>\u041c\u0430\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 (magic links) \u0438 \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u0430\u0440\u043e\u043b\u0438 \u2014 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u0441\u0441\u044b\u043b\u043a\u0438 \u0438\u043b\u0438 \u043a\u043e\u0434\u0430 \u043f\u043e email.<\/li>\n<li>OAuth \u2014 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0447\u0435\u0440\u0435\u0437 Github \/ Google \/ Apple \u0438 \u0442.\u0434.<\/li>\n<li>\u041f\u0430\u0440\u043e\u043b\u0438, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0441\u0431\u0440\u043e\u0441 \u043f\u0430\u0440\u043e\u043b\u044f \u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 email.<\/li>\n<\/ol>\n<p> <\/p>\n<p>\u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043d\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0433\u043e\u0442\u043e\u0432\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 UI, \u043d\u043e \u043d\u0438\u0447\u0442\u043e \u043d\u0435 \u043c\u0435\u0448\u0430\u0435\u0442 \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0438\u0437 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<p> <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>, \u0447\u0442\u043e Convex Auth \u2014 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0435\u0442\u0435\u0440\u043f\u0435\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f.<\/p>\n<p> <\/p>\n<p><strong>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442<\/strong><\/p>\n<p> <\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b:<\/p>\n<p> <\/p>\n<pre><code class=\"bash\">npm i @convex-dev\/auth @auth\/core<\/code><\/pre>\n<p> <\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438:<\/p>\n<p> <\/p>\n<pre><code class=\"bash\">npx @convex-dev\/auth<\/code><\/pre>\n<p> <\/p>\n<p>\u042d\u0442\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0435\u043a\u0442 \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u0422\u0430\u043a\u0443\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 <a href=\"https:\/\/labs.convex.dev\/auth\/setup\/manual\">\u0432\u0440\u0443\u0447\u043d\u0443\u044e<\/a>.<\/p>\n<p> <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432 \u0441\u0445\u0435\u043c\u0443:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ convex\/schema.ts import { defineSchema } from 'convex\/server' import { authTables } from '@convex-dev\/auth\/server'  const schema = defineSchema({   ...authTables,   \/\/ \u0414\u0440\u0443\u0433\u0438\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b })  export default schema<\/code><\/pre>\n<p> <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430.<\/p>\n<p> <\/p>\n<p><em>Vite<\/em><\/p>\n<p> <\/p>\n<p>\u0417\u0430\u043c\u0435\u043d\u044f\u0435\u043c <code>ConvexProvider<\/code> \u0438\u0437 <code>convex\/react<\/code> \u043d\u0430 <code>ConvexAuthProvider<\/code> \u0438\u0437 <code>@convex-dev\/auth-react<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { ConvexAuthProvider } from '@convex-dev\/auth\/react' import React from 'react' import ReactDOM from 'react-dom\/client' import { ConvexReactClient } from 'convex\/react' import App from '.\/App.tsx' import '.\/index.css'  const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string)  ReactDOM.createRoot(document.getElementById('root')!).render(   &lt;React.StrictMode&gt;     &lt;ConvexAuthProvider client={convex}&gt;       &lt;App \/&gt;     &lt;\/ConvexAuthProvider&gt;   &lt;\/React.StrictMode&gt;, )<\/code><\/pre>\n<p> <\/p>\n<p><em>Next.js<\/em><\/p>\n<p> <\/p>\n<p>\u041e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 <code>ConvexAuthNextjsServerProvider<\/code> \u0438\u0437 <code>@convex-dev\/auth\/nextjs\/server<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ app\/layout.tsx import { ConvexAuthNextjsServerProvider } from '@convex-dev\/auth\/nextjs\/server'  export default function RootLayout({   children, }: Readonly&lt;{   children: React.ReactNode }&gt;) {   return (     &lt;ConvexAuthNextjsServerProvider&gt;       &lt;html lang='en'&gt;         &lt;body&gt;{children}&lt;\/body&gt;       &lt;\/html&gt;     &lt;\/ConvexAuthNextjsServerProvider&gt;   ) }<\/code><\/pre>\n<p> <\/p>\n<p>\u0412 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0435 \u0434\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c <code>ConvexProvider<\/code> \u0438\u0437 <code>convex\/react<\/code> \u043d\u0430 <code>ConvexAuthNextjsProvider<\/code> \u0438\u0437 <code>@convex-dev\/auth\/nextjs<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ app\/ConvexClientProvider.tsx 'use client'  import { ConvexAuthNextjsProvider } from '@convex-dev\/auth\/nextjs' import { ConvexReactClient } from 'convex\/react' import { ReactNode } from 'react'  const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!)  export function ConvexClientProvider({ children }: { children: ReactNode }) {   return (     &lt;ConvexAuthNextjsProvider client={convex}&gt;       {children}     &lt;\/ConvexAuthNextjsProvider&gt;   ) }<\/code><\/pre>\n<p> <\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c \u0432 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0432\u0441\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ app\/layout.tsx import { ConvexAuthNextjsServerProvider } from '@convex-dev\/auth\/nextjs\/server' import { ConvexClientProvider } from '.\/ConvexClientProvider'  export default function RootLayout({   children, }: Readonly&lt;{   children: React.ReactNode }&gt;) {   return (     &lt;ConvexAuthNextjsServerProvider&gt;       &lt;html lang='en'&gt;         &lt;body&gt;           &lt;ConvexClientProvider&gt;{children}&lt;\/ConvexClientProvider&gt;         &lt;\/body&gt;       &lt;\/html&gt;     &lt;\/ConvexAuthNextjsServerProvider&gt;   ) }<\/code><\/pre>\n<p> <\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0444\u0430\u0439\u043b <code>middleware.ts<\/code>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>convexAuthNextjsMiddleware<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { convexAuthNextjsMiddleware } from '@convex-dev\/auth\/nextjs\/server'  export default convexAuthNextjsMiddleware()  export const config = {   \/\/ \u041f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0440\u043e\u0443\u0442\u043e\u0432,   \/\/ \u043a\u0440\u043e\u043c\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432   matcher: ['\/((?!.*\\\\..*|_next).*)', '\/', '\/(api|trpc)(.*)'], }<\/code><\/pre>\n<p> <\/p>\n<p><em>React Native<\/em><\/p>\n<p> <\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c <a href=\"https:\/\/docs.expo.dev\/versions\/latest\/sdk\/securestore\/\">expo-secure-store<\/a>:<\/p>\n<p> <\/p>\n<pre><code class=\"bash\">npx expo install expo-secure-store<\/code><\/pre>\n<p> <\/p>\n<p>\u0417\u0430\u043c\u0435\u043d\u044f\u0435\u043c <code>ConvexProvider<\/code> \u0438\u0437 <code>convex\/react<\/code> \u043d\u0430 <code>ConvexAuthProvider<\/code> \u0438\u0437 <code>@convex-dev\/auth-react<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ app\/_layout.tsx import { ConvexAuthProvider } from '@convex-dev\/auth\/react' import { ConvexReactClient } from 'convex\/react' import { Stack } from 'expo-router' import * as SecureStore from 'expo-secure-store'  const convex = new ConvexReactClient(process.env.EXPO_PUBLIC_CONVEX_URL!, {   unsavedChangesWarning: false, })  const secureStorage = {   getItem: SecureStore.getItemAsync,   setItem: SecureStore.setItemAsync,   removeItem: SecureStore.deleteItemAsync, }  export default function RootLayout() {   return (     &lt;ConvexAuthProvider client={convex} storage={secureStorage}&gt;       &lt;Stack&gt;         &lt;Stack.Screen name='index' \/&gt;       &lt;\/Stack&gt;     &lt;\/ConvexAuthProvider&gt;   ) }<\/code><\/pre>\n<p> <\/p>\n<h3 id=\"oauth\">OAuth<\/h3>\n<p> <\/p>\n<p>\u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 2 \u044d\u0442\u0430\u043f\u0430:<\/p>\n<p> <\/p>\n<ol>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b (GitHub, Google, Apple \u0438 \u0442.\u0434.).<\/li>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b, \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u043d\u0435\u043c.<\/li>\n<\/ol>\n<p> <\/p>\n<p>Convex Auth \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0439 \u043e\u0431\u043c\u0435\u043d \u0441\u0435\u043a\u0440\u0435\u0442\u0430\u043c\u0438 \u043c\u0435\u0436\u0434\u0443 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u043c \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0438 \u043d\u0430\u0448\u0438\u043c \u0431\u044d\u043a\u043e\u043c.<\/p>\n<p> <\/p>\n<p><strong>\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u044b<\/strong><\/p>\n<p> <\/p>\n<p>Convex Auth \u043e\u0441\u043d\u043e\u0432\u0430\u043d \u043d\u0430 <a href=\"https:\/\/authjs.dev\/\">Auth.js<\/a>.<\/p>\n<p> <\/p>\n<p>Auth.js \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c 80 \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432 OAuth.<\/p>\n<p> <\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 OAuth \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e GitHub, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043e\u043d\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0430\u043c\u043e\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439.<\/p>\n<p> <\/p>\n<p><strong>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430<\/strong><\/p>\n<p> <\/p>\n<p><em>Callback URL<\/em><\/p>\n<p> <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0443 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430, \u043c\u044b \u043e\u0431\u044b\u0447\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0435\u043c &#171;\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435&#187; (app) \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a OAuth.<\/p>\n<p> <\/p>\n<p>\u0421\u0440\u0435\u0434\u0438 \u043f\u0440\u043e\u0447\u0435\u0433\u043e, \u043e\u0431\u044b\u0447\u043d\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c callback URL, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u0440\u0443\u0433\u0438\u0435 URL\/\u0434\u043e\u043c\u0435\u043d\u044b.<\/p>\n<p> <\/p>\n<p>\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c (\u0434\u043e\u043c\u0435\u043d\u043e\u043c) callback URL \u0434\u043b\u044f Convex Auth \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f HTTP Actions URL, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432 \u043f\u0430\u043d\u0435\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f. \u041e\u043d \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441 <code>CONVEX_URL<\/code>, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0434\u043e\u043c\u0435\u043d\u0430 \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>.site<\/code>, \u0430 \u043d\u0435 <code>.cloud<\/code>.<\/p>\n<p> <\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u0434\u0435\u043f\u043b\u043e\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>fast-horse-123<\/code>, \u0442\u043e HTTP Actions URL \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043a\u0430\u043a <code>https:\/\/fast-horse-123.convex.site<\/code>, \u0430 callback URL \u0434\u043b\u044f GitHub \u0442\u0430\u043a:<\/p>\n<p> <\/p>\n<pre><code class=\"plaintext\">https:\/\/fast-horse-123.convex.site\/api\/auth\/callback\/github<\/code><\/pre>\n<p> <\/p>\n<p><em>\u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0440\u0435\u0434\u044b<\/em><\/p>\n<p> <\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0441\u0440\u0435\u0434\u044b \u0434\u043b\u044f GitHub:<\/p>\n<p> <\/p>\n<pre><code class=\"bash\">npx convex env set AUTH_GITHUB_ID yourgithubclientid npx convex env set AUTH_GITHUB_SECRET yourgithubsecret<\/code><\/pre>\n<p> <\/p>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043f\u0440\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0441\u0440\u0435\u0434\u044b \u0432 Convex \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/docs.convex.dev\/production\/environment-variables#setting-environment-variables\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p> <\/p>\n<p><em>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430<\/em><\/p>\n<p> <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0432 \u043c\u0430\u0441\u0441\u0438\u0432 <code>providers<\/code> \u0432 \u0444\u0430\u0439\u043b\u0435 <code>convex\/auth.ts<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import GitHub from '@auth\/core\/providers\/github' import { convexAuth } from '@convex-dev\/auth\/server'  export const { auth, signIn, signOut, store } = convexAuth({   providers: [GitHub], })<\/code><\/pre>\n<p> <\/p>\n<p><em>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u043d\u043e\u043f\u043a\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/em><\/p>\n<p> <\/p>\n<p>\u041f\u043e\u0442\u043e\u043a (flow) \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>signIn<\/code>, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0439 \u0445\u0443\u043a\u043e\u043c <code>useAuthActions<\/code>.<\/p>\n<p> <\/p>\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0418\u0414 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u2014 \u0435\u0433\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0432 \u043d\u0438\u0436\u043d\u0435\u043c \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0435 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e):<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { useAuthActions } from '@convex-dev\/auth\/react'  export function SignIn() {   const { signIn } = useAuthActions()    return (     &lt;button onClick={() =&gt; void signIn('github')}&gt;       \u0412\u043e\u0439\u0442\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e GitHub     &lt;\/button&gt;   ) }<\/code><\/pre>\n<p> <\/p>\n<p><em>\u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/em><\/p>\n<p> <\/p>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 <code>users<\/code> \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e <code>name<\/code>, <code>email<\/code> \u0438 <code>image<\/code> \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<p> <\/p>\n<p>\u042d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <code>profile<\/code> \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import GitHub from '@auth\/core\/providers\/github' import { convexAuth } from '@convex-dev\/auth\/server'  export const { auth, signIn, signOut, store } = convexAuth({   providers: [     GitHub({       profile(githubProfile, tokens) {         return {           id: githubProfile.id,           name: githubProfile.name,           email: githubProfile.email,           image: githubProfile.picture,           githubId: githubProfile.id,         }       },     }),   ], })<\/code><\/pre>\n<p> <\/p>\n<p>\u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0435 <code>githubId<\/code>. \u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0445\u0435\u043c\u0443 \u0411\u0414.<\/p>\n<p> <\/p>\n<p><code>profile()<\/code> \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043f\u043e\u043b\u0435 <code>id<\/code> \u0441 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u0418\u0414, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430.<\/p>\n<p> <\/p>\n<h3 id=\"paroli\">\u041f\u0430\u0440\u043e\u043b\u0438<\/h3>\n<p> <\/p>\n<p>\u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043e\u0441\u043d\u043e\u0432\u0430\u043d \u043d\u0430 \u0441\u0435\u043a\u0440\u0435\u0442\u043d\u043e\u043c \u043f\u0430\u0440\u043e\u043b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<p> <\/p>\n<p>\u0421\u043e\u043b\u0438\u0434\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043f\u0430\u0440\u043e\u043b\u044f \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442 \u0441\u043f\u043e\u0441\u043e\u0431 \u0441\u0431\u0440\u043e\u0441\u0430 \u043f\u0430\u0440\u043e\u043b\u044f (\u043e\u0431\u044b\u0447\u043d\u043e \u0447\u0435\u0440\u0435\u0437 email \u0438\u043b\u0438 \u043a\u043e\u0434).<\/p>\n<p> <\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 email (\u043f\u0440\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u043f\u043e\u0441\u043b\u0435) \u0434\u043b\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u0438 email.<\/p>\n<p> <\/p>\n<p><em>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430<\/em><\/p>\n<p> <\/p>\n<p>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e email (\u0438\u043b\u0438 \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f) \u0438 \u043f\u0430\u0440\u043e\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 <code>Password<\/code>.<\/p>\n<p> <\/p>\n<p>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { Password } from '@convex-dev\/auth\/providers\/Password' import { convexAuth } from '@convex-dev\/auth\/server'  export const { auth, signIn, signOut, store } = convexAuth({   providers: [Password], })<\/code><\/pre>\n<p> <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e\/\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u043f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0444\u043e\u0440\u043c\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>signIn<\/code>.<\/p>\n<p> <\/p>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u2014 \u044d\u0442\u043e \u0440\u0430\u0437\u043d\u044b\u0435 \u0432\u0435\u0449\u0438. \u0422\u043e, \u043a\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u043e\u043b\u044f <code>flow<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { useAuthActions } from '@convex-dev\/auth\/react'  export function SignIn() {   const { signIn } = useAuthActions()   \/\/ \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438\u043b\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f?   const [step, setStep] = useState&lt;'signUp' | 'signIn'&gt;('signIn')    return (     &lt;form       onSubmit={(event) =&gt; {         event.preventDefault()         const formData = new FormData(event.currentTarget)         void signIn('password', formData)       }}     &gt;       &lt;input name='email' placeholder='Email' type='text' \/&gt;       &lt;input name='password' placeholder='Password' type='password' \/&gt;       \/\/ \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438\u043b\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f? formaData.get(\"step\")       &lt;input name='flow' value={step} type='hidden' \/&gt;       &lt;button type='submit'&gt;         {step === 'signIn' ? '\u0412\u043e\u0439\u0442\u0438' : '\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f'}       &lt;\/button&gt;       &lt;button         type='button'         onClick={() =&gt; {           setStep(step === 'signIn' ? 'signUp' : 'signIn')         }}       &gt;         {step === 'signIn' ? '\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f' : '\u0412\u0445\u043e\u0434'}       &lt;\/button&gt;     &lt;\/form&gt;   ) }<\/code><\/pre>\n<p> <\/p>\n<p><em>\u0421\u0431\u0440\u043e\u0441 \u043f\u0430\u0440\u043e\u043b\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e email<\/em><\/p>\n<p> <\/p>\n<p>\u0421\u0431\u0440\u043e\u0441 \u043f\u0430\u0440\u043e\u043b\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e email \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f 2 \u044d\u0442\u0430\u043f\u0430:<\/p>\n<p> <\/p>\n<ol>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u0441\u0441\u044b\u043b\u043a\u0443 \u0434\u043b\u044f \u0441\u0431\u0440\u043e\u0441\u0430 \u043f\u0430\u0440\u043e\u043b\u044f \u043d\u0430 email.<\/li>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435 \u0438\u043b\u0438 \u0432\u0432\u043e\u0434\u0438\u0442 \u043a\u043e\u0434 \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 \u0438 \u0432\u0432\u043e\u0434\u0438\u0442 \u043d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c.<\/li>\n<\/ol>\n<p> <\/p>\n<p>\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 <code>Password<\/code> \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0441\u0431\u0440\u043e\u0441 \u043f\u0430\u0440\u043e\u043b\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 <code>reset<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 email.<\/p>\n<p> <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 email:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import Resend from '@auth\/core\/providers\/resend' import { Resend as ResendAPI } from 'resend' import { alphabet, generateRandomString } from 'oslo\/crypto'  export const ResendOTPPasswordReset = Resend({   id: 'resend-otp',   apiKey: process.env.AUTH_RESEND_KEY,   async generateVerificationToken() {     return generateRandomString(8, alphabet('0-9'))   },   async sendVerificationRequest({ identifier: email, provider, token }) {     const resend = new ResendAPI(provider.apiKey)     const { error } = await resend.emails.send({       from: 'My App &lt;onboarding@resend.dev&gt;',       to: [email],       subject: '\u0421\u0431\u0440\u043e\u0441 \u043f\u0430\u0440\u043e\u043b\u044f',       text: `\u041a\u043e\u0434 \u0434\u043b\u044f \u0441\u0431\u0440\u043e\u0441\u0430 \u043f\u0430\u0440\u043e\u043b\u044f: ${token}`,     })      if (error) {       throw new Error('\u041f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u0434\u043b\u044f \u0441\u0431\u0440\u043e\u0441\u0430 \u043f\u0430\u0440\u043e\u043b\u044f \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430')     }   }, })<\/code><\/pre>\n<p> <\/p>\n<p>\u0418 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0435\u0433\u043e \u0432 <code>convex\/auth.ts<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { Password } from '@convex-dev\/auth\/providers\/Password' import { convexAuth } from '@convex-dev\/auth\/server' import { ResendOTPPasswordReset } from '.\/ResendOTPPasswordReset'  export const { auth, signIn, signOut, store } = convexAuth({   providers: [Password({ reset: ResendOTPPasswordReset })], })<\/code><\/pre>\n<p> <\/p>\n<p>\u041f\u043e\u0442\u043e\u043a \u0441\u0431\u0440\u043e\u0441\u0430 \u043f\u0430\u0440\u043e\u043b\u044f \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 <code>\"reset\"<\/code> \u0438 <code>\"reset-verification\"<\/code> \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 <code>flow<\/code> \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>signIn<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { useAuthActions } from '@convex-dev\/auth\/react'  export function PasswordReset() {   const { signIn } = useAuthActions()   const [step, setStep] = useState&lt;'forgot' | { email: string }&gt;('forgot')    return step === 'forgot' ? (     &lt;form       onSubmit={(event) =&gt; {         event.preventDefault()         const formData = new FormData(event.currentTarget)         void signIn('password', formData).then(() =&gt;           setStep({ email: formData.get('email') as string }),         )       }}     &gt;       &lt;input name='email' placeholder='Email' type='text' \/&gt;       \/\/ \u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0441\u0431\u0440\u043e\u0441       &lt;input name='flow' value='reset' type='hidden' \/&gt;       &lt;button type='submit'&gt;\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u0434&lt;\/button&gt;     &lt;\/form&gt;   ) : (     &lt;form       onSubmit={(event) =&gt; {         event.preventDefault()         const formData = new FormData(event.currentTarget)         void signIn('password', formData)       }}     &gt;       &lt;input name='email' value={step.email} type='hidden' \/&gt;       \/\/ \u041a\u043e\u0434       &lt;input name='code' placeholder='Code' type='text' \/&gt;       \/\/ \u041d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u043e\u043b\u044c       &lt;input name='newPassword' placeholder='New password' type='password' \/&gt;       \/\/ \u0417\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u043c \u0441\u0431\u0440\u043e\u0441       &lt;input name='flow' value='reset-verification' type='hidden' \/&gt;       &lt;button type='submit'&gt;\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c&lt;\/button&gt;       &lt;button type='button' onClick={() =&gt; setStep('signIn')}&gt;         \u041e\u0442\u043c\u0435\u043d\u0430       &lt;\/button&gt;     &lt;\/form&gt;   ) }<\/code><\/pre>\n<p> <\/p>\n<p><em>\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 email<\/em><\/p>\n<p> <\/p>\n<p>\u041f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 <code>Password<\/code> \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 email \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 <code>verify<\/code>, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0435\u0439 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 email.<\/p>\n<p> <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 email:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import Resend from '@auth\/core\/providers\/resend' import { Resend as ResendAPI } from 'resend' import { alphabet, generateRandomString } from 'oslo\/crypto'  export const ResendOTP = Resend({   id: 'resend-otp',   apiKey: process.env.AUTH_RESEND_KEY,   async generateVerificationToken() {     return generateRandomString(8, alphabet('0-9'))   },   async sendVerificationRequest({ identifier: email, provider, token }) {     const resend = new ResendAPI(provider.apiKey)     const { error } = await resend.emails.send({       from: 'My App &lt;onboarding@resend.dev&gt;',       to: [email],       subject: '\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 email',       text: `\u0412\u0430\u0448 \u043a\u043e\u0434: ${token}`,     })      if (error) {       throw new Error(         '\u041f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0441\u0441\u044b\u043b\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f email \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430',       )     }   }, })<\/code><\/pre>\n<p> <\/p>\n<p>\u0418 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0435\u0433\u043e \u0432 <code>convex\/auth.ts<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { Password } from '@convex-dev\/auth\/providers\/Password' import { convexAuth } from '@convex-dev\/auth\/server' import { ResendOTP } from '.\/ResendOTP'  export const { auth, signIn, signOut, store } = convexAuth({   providers: [Password({ verify: ResendOTP })], })<\/code><\/pre>\n<p> <\/p>\n<p><code>signIn()<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043c\u044b \u044d\u0442\u043e \u043d\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u043c \u043f\u043e\u043b\u043d\u043e\u0435 \u0440\u0430\u0437\u043c\u043e\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u043f\u043e\u0441\u043b\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { useAuthActions } from '@convex-dev\/auth\/react'  export function SignIn() {   const { signIn } = useAuthActions()   const [step, setStep] = useState&lt;'signIn' | 'signUp' | { email: string }&gt;(     'signIn',   )    return step === 'signIn' || step === 'signUp' ? (     &lt;form       onSubmit={(event) =&gt; {         event.preventDefault()         const formData = new FormData(event.currentTarget)         void signIn('password', formData).then(() =&gt;           setStep({ email: formData.get('email') as string }),         )       }}     &gt;       &lt;input name='email' placeholder='Email' type='text' \/&gt;       &lt;input name='password' placeholder='Password' type='password' \/&gt;       &lt;input name='flow' value={step} type='hidden' \/&gt;       &lt;button type='submit'&gt;         {step === 'signIn' ? '\u0412\u043e\u0439\u0442\u0438' : '\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f'}       &lt;\/button&gt;       &lt;button         type='button'         onClick={() =&gt; {           setStep(step === 'signIn' ? 'signUp' : 'signIn')         }}       &gt;         {step === 'signIn' ? '\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f' : '\u0412\u0445\u043e\u0434'}       &lt;\/button&gt;     &lt;\/form&gt;   ) : (     &lt;form       onSubmit={(event) =&gt; {         event.preventDefault()         const formData = new FormData(event.currentTarget)         void signIn('password', formData)       }}     &gt;       &lt;input name='code' placeholder='Code' type='text' \/&gt;       &lt;input name='email' value={step.email} type='hidden' \/&gt;       &lt;button type='submit'&gt;\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c&lt;\/button&gt;       &lt;button type='button' onClick={() =&gt; setStep('signIn')}&gt;         \u041e\u0442\u043c\u0435\u043d\u0430       &lt;\/button&gt;     &lt;\/form&gt;   ) }<\/code><\/pre>\n<p> <\/p>\n<p><em>\u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f email \u0438 \u043f\u0430\u0440\u043e\u043b\u044f<\/em><\/p>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u0432\u043e\u0434\u0438\u043c\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c, \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c\u0438:<\/p>\n<p> <\/p>\n<ul>\n<li><a href=\"https:\/\/zod.dev\/\">Zod<\/a> \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 email \u0438 \u0434\u043b\u0438\u043d\u044b \u043f\u0430\u0440\u043e\u043b\u044f, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438 \u043c\u0435\u0436\u0434\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c<\/li>\n<li><a href=\"https:\/\/haveibeenpwned.com\/\">haveibeenpwned<\/a> \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0443\u0442\u0435\u0447\u043a\u0438 email<\/li>\n<li><a href=\"https:\/\/zxcvbn-ts.github.io\/zxcvbn\/\">zxcvbn-ts<\/a> \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 &#171;\u0441\u0438\u043b\u044b&#187; \u043f\u0430\u0440\u043e\u043b\u044f<\/li>\n<\/ul>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 <code>profile<\/code> \u0432 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 <code>Password<\/code>.<\/p>\n<p> <\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f Zod:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { Password } from '@convex-dev\/auth\/providers\/Password' import { z } from 'zod'  const ParamsSchema = z.object({   email: z.string().email(),   password: z.string().min(16), })  export default Password({   profile(params) {     const { error, data } = ParamsSchema.safeParse(params)     if (error) {       throw new ConvexError(error.format())     }     return { email: data.email }   }, })<\/code><\/pre>\n<p> <\/p>\n<h3 id=\"avtorizaciya\">\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<p> <\/p>\n<p><strong>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f<\/strong><\/p>\n<p> <\/p>\n<p>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<p> <\/p>\n<p><strong>\u0412\u044b\u0445\u043e\u0434 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/strong><\/p>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u0432\u044b\u0445\u043e\u0434\u0430 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>signOut<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { useAuthActions } from '@convex-dev\/auth\/react'  export function SignOut() {   const { signOut } = useAuthActions()    return &lt;button onClick={signOut}&gt;\u0412\u044b\u0445\u043e\u0434&lt;\/button&gt; }<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438<\/strong><\/p>\n<p> <\/p>\n<p><code>convex\/react<\/code> \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { Authenticated, Unauthenticated, AuthLoading } from 'convex\/react' import { SignIn } from '.\/SignIn' import { SignOut } from '.\/SignOut'  export function App() {   return (     &lt;&gt;       &lt;AuthLoading&gt;{\/* \u0418\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 *\/}&lt;\/AuthLoading&gt;       &lt;Unauthenticated&gt;         &lt;SignIn \/&gt;       &lt;\/Unauthenticated&gt;       &lt;Authenticated&gt;         &lt;SignOut \/&gt;         &lt;Content \/&gt;       &lt;\/Authenticated&gt;     &lt;\/&gt;   ) }  function Content() {   \/* \u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 *\/ }<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 HTTP<\/strong><\/p>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 HTTP \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u043a\u0435\u043d JWT, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0445\u0443\u043a\u043e\u043c <code>useAuthToken<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { useAuthToken } from '@convex-dev\/auth\/react'  function SomeComponent() {   const token = useAuthToken()    const onClick = async () =&gt; {     const response = await fetch(       `${process.env.VITE_CONVEX_SITE_URL!}\/someEndpoint`,       \/\/ \u041f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438       { headers: { Authorization: `Bearer ${token}` } },     )     \/\/ ...   }   \/\/ ... }<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0445 \u0431\u044d\u043a\u0435\u043d\u0434\u0430<\/strong><\/p>\n<p> <\/p>\n<p>\u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0445 Convex \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0438\u0437 <code>@convex-dev\/auth\/server<\/code>.<\/p>\n<p> <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u0438 <code>getAuthUserId<\/code> \u0438 <code>getAuthSessionId<\/code> \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>ctx.auth.getUserIdentity<\/code> \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0430\u043f\u0438.<\/p>\n<p> <\/p>\n<p><strong>\u041c\u043e\u0434\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u044b\u0445<\/strong><\/p>\n<p> <\/p>\n<p>Convex Auth \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u044b <code>users<\/code> \u0438 <code>authSession<\/code>.<\/p>\n<p> <\/p>\n<p>\u041f\u0440\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 <code>users<\/code>.<\/p>\n<p> <\/p>\n<p>\u041f\u0440\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 <code>authSessions<\/code>. \u042d\u0442\u043e\u0442 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u044f \u0441\u0435\u0441\u0441\u0438\u0438 \u0438\u043b\u0438 \u0432\u044b\u0445\u043e\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b.<\/p>\n<p> <\/p>\n<p>\u041e\u0434\u0438\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e. \u0414\u043b\u044f \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043e\u0434\u043d\u0430 \u0441\u0435\u0441\u0441\u0438\u044f \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u0432\u0441\u0435\u043c\u0438 \u0432\u043a\u043b\u0430\u0434\u043a\u0430\u043c\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.<\/p>\n<p> <\/p>\n<p><strong>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0418\u0414 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/strong><\/p>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0418\u0414 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f <code>getAuthUserId()<\/code> \u0441 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c <code>ctx<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { getAuthUserId } from '@convex-dev\/auth\/server' import { query } from '.\/_generated\/server'  export const currentUser = query({   args: {},   handler: async (ctx) =&gt; {     const userId = await getAuthUserId(ctx)     if (userId === null) {       return null     }     return await ctx.db.get(userId)   }, })<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0418\u0414 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0441\u0435\u0441\u0441\u0438\u0438<\/strong><\/p>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0418\u0414 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f <code>getAuthSessionId()<\/code> \u0441 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c <code>ctx<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { getAuthSessionId } from '@convex-dev\/auth\/server' import { query } from '.\/_generated\/server'  export const currentSession = query({   args: {},   handler: async (ctx) =&gt; {     const sessionId = await getAuthSessionId(ctx)     if (sessionId === null) {       return null     }     return await ctx.db.get(sessionId)   }, })<\/code><\/pre>\n<p> <\/p>\n<h3 id=\"servernaya-autentifikaciya-v-nextjs\">\u0421\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432 Next.js<\/h3>\n<p> <\/p>\n<p><strong>\u0417\u0430\u0449\u0438\u0442\u0430 \u0440\u043e\u0443\u0442\u043e\u0432<\/strong><\/p>\n<p> <\/p>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0432\u0441\u0435 \u0440\u043e\u0443\u0442\u044b \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0431\u0435\u0437 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0417\u0430\u0449\u0438\u0442\u0438\u0442\u044c \u0440\u043e\u0443\u0442\u044b \u043e\u0442 \u043d\u0435\u0441\u0430\u043d\u043a\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043c\u043e\u0436\u043d\u043e \u0432 \u0444\u0430\u0439\u043b\u0435 <code>middleware.ts<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import {   convexAuthNextjsMiddleware,   createRouteMatcher,   isAuthenticatedNextjs,   nextjsMiddlewareRedirect, } from '@convex-dev\/auth\/nextjs\/server'  const isSignInPage = createRouteMatcher(['\/signin']) const isProtectedRoute = createRouteMatcher(['\/product(.*)'])  export default convexAuthNextjsMiddleware((request) =&gt; {   if (isSignInPage(request) &amp;&amp; isAuthenticatedNextjs()) {     return nextjsMiddlewareRedirect(request, '\/product')   }   if (isProtectedRoute(request) &amp;&amp; !isAuthenticatedNextjs()) {     return nextjsMiddlewareRedirect(request, '\/signin')   } })  export const config = {   \/\/ \u041f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0440\u043e\u0443\u0442\u043e\u0432,   \/\/ \u043a\u0440\u043e\u043c\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432   matcher: ['\/((?!.*\\\\..*|_next).*)', '\/', '\/(api|trpc)(.*)'], }<\/code><\/pre>\n<p> <\/p>\n<p>Convex Auth \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0430\u043f\u0438 \u0438 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u0430:<\/p>\n<p> <\/p>\n<ul>\n<li><code>createRouteMatcher<\/code> \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441, \u0447\u0442\u043e \u0438 <code>config<\/code> \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u0430. \u041c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0435\u0435 \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u043c \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u0432, \u0438 \u043e\u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 <code>NextRequest<\/code> \u043f\u0440\u0438 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0438 \u0440\u043e\u0443\u0442\u0430<\/li>\n<li>\u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>isAuthenticatedNextjs<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0441\u0442\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u041f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 <code>ConvexAuthNextjsServerProvider<\/code> \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u043a\u0430\u043a \u0432 http-only cookies, \u0442\u0430\u043a \u0438 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u043d\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b<\/li>\n<li>\u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>nextjsMiddlewareRedirect<\/code> \u2014 \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0439<\/li>\n<\/ul>\n<p> <\/p>\n<pre><code class=\"javascript\">export function nextjsMiddlewareRedirect(   request: NextRequest,   pathname: string, ) {   const url = request.nextUrl.clone()   url.pathname = pathname   return NextResponse.redirect(url) }<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u041f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0438 \u043e\u0431\u044b\u0447\u043d\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445<\/strong><\/p>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438 \u043e\u0431\u044b\u0447\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 Next.js \u0438\u0437 \u0431\u044d\u043a\u0435\u043d\u0434\u0430 Convex \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>preloadQuery<\/code> \u0438 <code>fetchQuery<\/code>, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>convexAuthNextjsToken<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { convexAuthNextjsToken } from '@convex-dev\/auth\/nextjs\/server' import { preloadQuery } from 'convex\/nextjs' import { api } from '@\/convex\/_generated\/api' import { Tasks } from '.\/Tasks'  export async function TasksWrapper() {   const preloadedTasks = await preloadQuery(     api.tasks.list,     { list: 'default' },     { token: convexAuthNextjsToken() },   )   return &lt;Tasks preloadedTasks={preloadedTasks} \/&gt; }<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u0412\u044b\u0437\u043e\u0432 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043c\u0443\u0442\u0430\u0446\u0438\u0439 \u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439<\/strong><\/p>\n<p> <\/p>\n<p>\u041c\u0443\u0442\u0430\u0446\u0438\u0438 \u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0433\u0443\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0437 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 (server actions) Next.js, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0438\u0437 POST \u0438 PUT-\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0440\u043e\u0443\u0442\u0430 (route handlers):<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { api } from '@\/convex\/_generated\/api' import { fetchMutation, fetchQuery } from 'convex\/nextjs' import { revalidatePath } from 'next\/cache'  export default async function PureServerPage() {   const tasks = await fetchQuery(api.tasks.list, { list: 'default' })    async function createTask(formData: FormData) {     'use server'      await fetchMutation(       api.tasks.create,       {         text: formData.get('text') as string,       },       { token: convexAuthNextjsToken() },     )     revalidatePath('\/example')   }    return &lt;form action={createTask}&gt;...&lt;\/form&gt; }<\/code><\/pre>\n<p> <\/p>\n<h2 id=\"-clerk\">\u276f Clerk<\/h2>\n<p> <\/p>\n<p><a href=\"https:\/\/clerk.com\/\">Clerk<\/a> \u2014 \u044d\u0442\u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0430\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u0438, \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432, \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0435 email \u0438\u043b\u0438 \u0421\u041c\u0421, \u043c\u0443\u043b\u044c\u0442\u0438\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438.<\/p>\n<p> <\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/get-convex\/convex-demos\/tree\/main\/clerk\">\u041f\u0440\u0438\u043c\u0435\u0440<\/a><\/li>\n<\/ul>\n<p> <\/p>\n<p><strong>\u041d\u0430\u0447\u0430\u043b\u043e \u0440\u0430\u0431\u043e\u0442\u044b<\/strong><\/p>\n<p> <\/p>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0435\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 React \u0441 Convex.<\/p>\n<p> <\/p>\n<p><em>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c\u0441\u044f \u0432 Clerk<\/em><\/p>\n<p> <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/vj\/dt\/3k\/vjdt3kfdyaw1m7zuttow7pgjkuy.png\" data-src=\"https:\/\/habrastorage.org\/webt\/vj\/dt\/3k\/vjdt3kfdyaw1m7zuttow7pgjkuy.png\"\/> <\/p>\n<p> <\/p>\n<p> <\/p>\n<p><em>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u0430\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/em><\/p>\n<p> <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/24\/f8\/oj\/24f8oj75yfvary5wnzfh_reir1g.png\" data-src=\"https:\/\/habrastorage.org\/webt\/24\/f8\/oj\/24f8oj75yfvary5wnzfh_reir1g.png\"\/> <\/p>\n<p> <\/p>\n<p> <\/p>\n<p><em>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0448\u0430\u0431\u043b\u043e\u043d JWT<\/em><\/p>\n<p> <\/p>\n<p>\u0412 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 JWT Templates \u043a\u043b\u0438\u043a\u0430\u0435\u043c \u043f\u043e New template \u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c Convex.<\/p>\n<p> <\/p>\n<p>\u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c Issuer URL.<\/p>\n<p> <\/p>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c Apply Changes.<\/p>\n<p> <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u0442\u043e\u043a\u0435\u043d JWT \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f <code>convex<\/code>.<\/p>\n<p> <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/jf\/ok\/ld\/jfokldzj9yy_govqdpazrp6t5_8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/jf\/ok\/ld\/jfokldzj9yy_govqdpazrp6t5_8.png\"\/> <\/p>\n<p> <\/p>\n<p> <\/p>\n<p><em>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438<\/em><\/p>\n<p> <\/p>\n<p>\u0412 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>convex<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>auth.config.ts<\/code> \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0442\u043e\u043a\u0435\u043d\u043e\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">export default {   providers: [     {       \/\/ Issuer URL       domain: \"https:\/\/your-issuer-url.clerk.accounts.dev\/\",       applicationID: \"convex\",     },   ] };<\/code><\/pre>\n<p> <\/p>\n<p><em>\u0414\u0435\u043f\u043b\u043e\u0438\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f<\/em><\/p>\n<p> <\/p>\n<pre><code class=\"bash\">npx convex dev<\/code><\/pre>\n<p> <\/p>\n<p><em>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c Clerk<\/em><\/p>\n<p> <\/p>\n<pre><code class=\"bash\">npm install @clerk\/clerk-react<\/code><\/pre>\n<p> <\/p>\n<p><em>\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c Publishable key \u0432 \u043f\u0430\u043d\u0435\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Clerk<\/em><\/p>\n<p> <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/c5\/8b\/6g\/c58b6gwph9xwukzt0d3tn_ku6b8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/c5\/8b\/6g\/c58b6gwph9xwukzt0d3tn_ku6b8.png\"\/> <\/p>\n<p> <\/p>\n<p> <\/p>\n<p><em>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c ConvexProviderWithClerk<\/em><\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ src\/main.tsx import React from \"react\"; import ReactDOM from \"react-dom\/client\"; import App from \".\/App\"; import \".\/index.css\"; import { ClerkProvider, useAuth } from \"@clerk\/clerk-react\"; import { ConvexProviderWithClerk } from \"convex\/react-clerk\"; import { ConvexReactClient } from \"convex\/react\";  const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);  ReactDOM.createRoot(document.getElementById(\"root\")!).render(   &lt;React.StrictMode&gt;     &lt;ClerkProvider publishableKey=\"pk_test_...\"&gt;       &lt;ConvexProviderWithClerk client={convex} useAuth={useAuth}&gt;         &lt;App \/&gt;       &lt;\/ConvexProviderWithClerk&gt;     &lt;\/ClerkProvider&gt;   &lt;\/React.StrictMode&gt;, );<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 UI \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438<\/strong><\/p>\n<p> <\/p>\n<p><code>convex\/react<\/code> \u0438 <code>@clerk\/clerk-react<\/code> \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { SignInButton, UserButton } from \"@clerk\/clerk-react\"; import { Authenticated, Unauthenticated, useQuery } from \"convex\/react\"; import { api } from \"..\/convex\/_generated\/api\";  function App() {   return (     &lt;main&gt;       &lt;Unauthenticated&gt;         &lt;SignInButton \/&gt;       &lt;\/Unauthenticated&gt;       &lt;Authenticated&gt;         &lt;UserButton \/&gt;         &lt;Content \/&gt;       &lt;\/Authenticated&gt;     &lt;\/main&gt;   ); }  function Content() {   const messages = useQuery(api.messages.getForCurrentUser);    return &lt;div&gt;\u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442: {messages?.length}&lt;\/div&gt;; }  export default App;<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0445 Convex<\/strong><\/p>\n<p> <\/p>\n<p>\u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d, \u0435\u0433\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 JWT \u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 <code>ctx.auth.getUserIdentity()<\/code>.<\/p>\n<p> <\/p>\n<p>\u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d, <code>ctx.auth.getUserIdentity()<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>null<\/code>.<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { query } from \".\/_generated\/server\";  export const getForCurrentUser = query({   args: {},   handler: async (ctx) =&gt; {     const identity = await ctx.auth.getUserIdentity();     if (identity === null) {       throw new Error(\"Unauthorized\");     }      return await ctx.db       .query(\"messages\")       .filter((q) =&gt; q.eq(q.field(\"author\"), identity.email))       .collect();   }, });<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0438 \u0432\u044b\u0445\u043e\u0434 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/strong><\/p>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u043e\u0442\u043e\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>SignInButton<\/code>. \u041f\u0440\u0438 \u0435\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u043d\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 Clerk:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { SignInButton } from \"@clerk\/clerk-react\";  function App() {   return (     &lt;div className=\"App\"&gt;       &lt;SignInButton mode=\"modal\" \/&gt;     &lt;\/div&gt;   ); }<\/code><\/pre>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u043f\u043e\u0442\u043e\u043a\u0430 \u0432\u044b\u0445\u043e\u0434\u0430 \u0438\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>SignOutButton<\/code> \u0438\u043b\u0438 <code>UserButton<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u043c\u043e\u0434\u0430\u043b\u043a\u0443 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u043a\u043d\u043e\u043f\u043a\u043e\u0439.<\/p>\n<p> <\/p>\n<p><strong>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f<\/strong><\/p>\n<p> <\/p>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0445\u0443\u043a <code>useConvexAuth<\/code> \u0432\u043c\u0435\u0441\u0442\u043e \u0445\u0443\u043a\u0430 <code>useAuth<\/code> \u043e\u0442 Clerk. <code>useConvexAuth()<\/code> \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0442\u043e\u043a\u0435\u043d \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0439 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0431\u044d\u043a\u0443 Convex, \u0430 \u0442\u0430\u043a\u0436\u0435, \u0447\u0442\u043e \u0431\u044d\u043a \u0435\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u043b:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { useConvexAuth } from \"convex\/react\";  function App() {   const { isLoading, isAuthenticated } = useConvexAuth();    return (     &lt;div className=\"App\"&gt;       {isAuthenticated ? \"\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\" : \"\u041d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\"}     &lt;\/div&gt;   ); }<\/code><\/pre>\n<p> <\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b <code>Authenticated<\/code>, <code>Unauthenticated<\/code> \u0438 <code>AuthLoading<\/code> \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0434\u043d\u043e\u0438\u043c\u0435\u043d\u043d\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 Clerk. \u042d\u0442\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0445\u0443\u043a <code>useConvex<\/code> \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { Authenticated, Unauthenticated, AuthLoading } from \"convex\/react\";  function App() {   return (     &lt;div className=\"App\"&gt;       &lt;Authenticated&gt;\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d&lt;\/Authenticated&gt;       &lt;Unauthenticated&gt;\u041d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d&lt;\/Unauthenticated&gt;       &lt;AuthLoading&gt;\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430...&lt;\/AuthLoading&gt;     &lt;\/div&gt;   ); }<\/code><\/pre>\n<p> <\/p>\n<p><em>\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 \u0432 React<\/em><\/p>\n<p> <\/p>\n<p>\u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0434\u0430\u043d\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0445\u0443\u043a\u0430 <code>useAuth<\/code> \u043e\u0442 Clerk:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { useUser } from \"@clerk\/clerk-react\";  export default function Badge() {   const { user } = useUser();   return &lt;span&gt;\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d \u043a\u0430\u043a {user.fullName}&lt;\/span&gt;; }<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u041f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c<\/strong><\/p>\n<p> <\/p>\n<p>\u041f\u043e\u0442\u043e\u043a \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<p> <\/p>\n<ol>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u0442 \u043a\u043d\u043e\u043f\u043a\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/li>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u0433\u0434\u0435 \u043e\u043d \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0435\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430, \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0433\u043e \u0432 Clerk.<\/li>\n<li>\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 Clerk \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u0443\u044e \u0432 \u043f\u0440\u043e\u043f\u0435 <code>afterSignIn<\/code>.<\/li>\n<li><code>ClerkProvider<\/code> \u0442\u0435\u043f\u0435\u0440\u044c \u0437\u043d\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d.<\/li>\n<li><code>ConvexProviderWithClerk<\/code> \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0442\u043e\u043a\u0435\u043d \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043e\u0442 Clerk.<\/li>\n<li><code>ConvexReactClient<\/code> \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u044d\u0442\u043e\u0442 \u0442\u043e\u043a\u0435\u043d \u0432 \u0431\u044d\u043a Convex \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438.<\/li>\n<li>Convex \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 \u0438\u0437 Clerk \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u044b \u0442\u043e\u043a\u0435\u043d\u0430.<\/li>\n<li><code>ConvexReactClient<\/code> \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 <code>ConvexProviderWithClerk<\/code> \u0442\u0435\u043f\u0435\u0440\u044c \u0437\u043d\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d \u0432 Convex. \u0425\u0443\u043a <code>useConvexAuth<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>isAuthenticated: true<\/code> \u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>Authenticated<\/code> \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442 \u0441\u0432\u043e\u0438\u0445 \u043f\u043e\u0442\u043e\u043c\u043a\u043e\u0432.<\/li>\n<\/ol>\n<p> <\/p>\n<p><code>ConvexProviderWithClerk<\/code> \u0437\u0430\u0431\u043e\u0442\u0438\u0442\u0441\u044f \u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0442\u043e\u043a\u0435\u043d\u0430 \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0441\u0442\u0430\u0432\u0430\u043b\u0441\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u0432 Convex.<\/p>\n<p> <\/p>\n<h2 id=\"-auth0\">\u276f Auth0<\/h2>\n<p> <\/p>\n<p><a href=\"https:\/\/auth0.com\/\">Auth0<\/a> \u2014 \u044d\u0442\u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0430\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u043f\u0430\u0440\u043e\u043b\u0438, \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432, \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0435 email \u0438\u043b\u0438 \u0421\u041c\u0421, \u043c\u0443\u043b\u044c\u0442\u0438\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e, SSO \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438.<\/p>\n<p> <\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/get-convex\/convex-demos\/tree\/main\/users-and-auth\">\u041f\u0440\u0438\u043c\u0435\u0440<\/a><\/li>\n<\/ul>\n<p> <\/p>\n<p>\u0412 \u0446\u0435\u043b\u043e\u043c, \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Auth0 \u043f\u043e\u0445\u043e\u0436\u0430 \u043d\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Clerk, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c <a href=\"https:\/\/docs.convex.dev\/auth\/auth0\">\u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0435\u0439<\/a>.<\/p>\n<p> <\/p>\n<h2 id=\"-autentifikaciya-v-funkciyah\">\u276f \u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0445<\/h2>\n<p> <\/p>\n<p>\u0414\u043e\u0441\u0442\u0443\u043f \u043a \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u0445 Convex \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <code>auth<\/code> \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <code>QueryCtx<\/code>, <code>MutationCtx<\/code> \u0438\u043b\u0438 <code>ActionCtx<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { mutation } from \".\/_generated\/server\";  export const myMutation = mutation({   args: {     \/\/ ...   },   handler: async (ctx, args) =&gt; {     const identity = await ctx.auth.getUserIdentity();     if (identity === null) {       throw new Error(\"Unauthorized\");     }     \/\/...   }, });<\/code><\/pre>\n<p> <\/p>\n<p><strong>UserIdentity<\/strong><\/p>\n<p> <\/p>\n<p>\u041e\u0431\u044a\u0435\u043a\u0442 <a href=\"https:\/\/docs.convex.dev\/api\/interfaces\/server.UserIdentity\">UserIdentity<\/a>, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0439 <code>getUserIdentity()<\/code>, \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043f\u043e\u043b\u044f <code>tokenIdentifier<\/code>, <code>subject<\/code> \u0438 <code>issuer<\/code>. \u041d\u0430\u043b\u0438\u0447\u0438\u0435 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u043e\u043b\u0435\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0442\u043e\u043a\u0435\u043d\u043e\u0432 JWT \u0438 <a href=\"https:\/\/openid.net\/specs\/openid-connect-core-1_0.html#StandardClaims\">\u043e\u0431\u043b\u0430\u0441\u0442\u0435\u0439 OpenID<\/a>.<\/p>\n<p> <\/p>\n<p><code>tokenIdentifier<\/code> \u2014 \u044d\u0442\u043e \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044f <code>subject<\/code> \u0438 <code>issuer<\/code>, \u0447\u0442\u043e \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0418\u0414, \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u0432.<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { mutation } from \".\/_generated\/server\";  export const myMutation = mutation({   args: {     \/\/ ...   },   handler: async (ctx, args) =&gt; {     const identity = await ctx.auth.getUserIdentity();     const { tokenIdentifier, name, email } = identity!;     \/\/...   }, });<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u041e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 HTTP<\/strong><\/p>\n<p> <\/p>\n<p><code>getUserIdentity()<\/code> \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0434\u0430\u043d\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u0445 HTTP \u0434\u043b\u044f \u0432\u044b\u0437\u043e\u0432\u0430 \u043a\u043e\u043d\u0435\u0447\u043d\u044b\u0445 \u0442\u043e\u0447\u0435\u043a \u0430\u043f\u0438 \u0441 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u043c <code>Authorization<\/code>:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">const jwtToken = \"...\";  fetch(\"https:\/\/&lt;deployment name&gt;.convex.site\/myAction\", {   headers: {     Authorization: `Bearer ${jwtToken}`,   }, });<\/code><\/pre>\n<p> <\/p>\n<h2 id=\"-hranenie-dannyh-polzovateley\">\u276f \u0425\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/h2>\n<p> <\/p>\n<p><em>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/em>: \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 Convex Auth \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f \u0432 \u0411\u0414 Convex \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438.<\/p>\n<p> <\/p>\n<p>\u0425\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u0445 \u0432 \u0411\u0414 Convex \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043f\u0440\u0438\u0447\u0438\u043d\u0430\u043c:<\/p>\n<p> <\/p>\n<ul>\n<li>\u043d\u0430\u0448\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c \u043d\u0443\u0436\u043d\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0434\u0440\u0443\u0433\u0438\u0445 \u044e\u0437\u0435\u0440\u0430\u0445, \u0430 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043e \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u043c<\/li>\n<li>\u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c \u043d\u0443\u0436\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043a \u043f\u043e\u043b\u044f\u043c, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u043c \u0432 <a href=\"https:\/\/docs.convex.dev\/auth\/functions-auth\">Open ID Connect JWT<\/a><\/li>\n<\/ul>\n<p> <\/p>\n<p>\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 2 \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u0445 \u0432 \u0411\u0414 (\u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0442\u043e\u0440\u043e\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0443\u044e\u0441\u044f \u0432 JWT):<\/p>\n<p> <\/p>\n<ol>\n<li>\u0412\u044b\u0437\u043e\u0432 \u043c\u0443\u0442\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0438\u0437 JWT, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0443\u044e \u0432 <code>ctx.auth<\/code>.<\/li>\n<li>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u0435\u0431-\u0445\u0443\u043a\u0430, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u043e\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.<\/li>\n<\/ol>\n<p> <\/p>\n<p><strong>\u0412\u044b\u0437\u043e\u0432 \u043c\u0443\u0442\u0430\u0446\u0438\u0438 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435<\/strong><\/p>\n<p> <\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/get-convex\/convex-demos\/tree\/main\/users-and-clerk\">\u041f\u0440\u0438\u043c\u0435\u0440<\/a><\/li>\n<\/ul>\n<p> <\/p>\n<p><em>\u0421\u0445\u0435\u043c\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 (\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e)<\/em><\/p>\n<p> <\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 <code>\"users\"<\/code>, \u0430 \u0442\u0430\u043a\u0436\u0435, \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e, \u0438\u043d\u0434\u0435\u043a\u0441 \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u044e\u0437\u0435\u0440\u043e\u0432 \u0432 \u0411\u0414.<\/p>\n<p> <\/p>\n<p>\u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>tokenIdentifier<\/code> \u0438\u0437 <code>ctx.auth.getUserIdentity()<\/code> \u0434\u043b\u044f \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u044e\u0437\u0435\u0440\u0430, \u043d\u043e \u0441 \u0442\u0435\u043c \u0436\u0435 \u0443\u0441\u043f\u0435\u0445\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>subject<\/code> \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 <code>email<\/code>.<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ convex\/schema.ts users: defineTable({   name: v.string(),   tokenIdentifier: v.string(), }).index(\"by_token\", [\"tokenIdentifier\"]),<\/code><\/pre>\n<p> <\/p>\n<p><em>\u041c\u0443\u0442\u0430\u0446\u0438\u044f \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/em><\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ convex\/users.ts import { mutation } from \".\/_generated\/server\";  export const store = mutation({   args: {},   handler: async (ctx) =&gt; {     const identity = await ctx.auth.getUserIdentity();     if (!identity) {       throw new Error(\"Called storeUser without authentication present\");     }      \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.     \/\/ \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435: \u0432\u043c\u0435\u0441\u0442\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c     \/\/ ctx.db.query(\"users\")     \/\/  .filter(q =&gt; q.eq(q.field(\"tokenIdentifier\"), identity.tokenIdentifier))     \/\/  .unique();     const user = await ctx.db       .query(\"users\")       .withIndex(\"by_token\", (q) =&gt;         q.eq(\"tokenIdentifier\", identity.tokenIdentifier),       )       .unique();     if (user !== null) {       \/\/ \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438       if (user.name !== identity.name) {         await ctx.db.patch(user._id, { name: identity.name });       }       return user._id;     }     \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f     return await ctx.db.insert(\"users\", {       name: identity.name ?? \"\u0410\u043d\u043e\u043d\u0438\u043c\u0443\u0441\",       tokenIdentifier: identity.tokenIdentifier,     });   }, });<\/code><\/pre>\n<p> <\/p>\n<p><em>\u0412\u044b\u0437\u043e\u0432 \u043c\u0443\u0442\u0430\u0446\u0438\u0438 \u0438\u0437 React<\/em><\/p>\n<p> <\/p>\n<p>\u041a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0445\u0443\u043a, \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u043c\u0443\u0442\u0430\u0446\u0438\u044e \u043f\u043e\u0441\u043b\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ src\/useStoreUserEffect.ts import { useUser } from \"@clerk\/clerk-react\"; import { useConvexAuth } from \"convex\/react\"; import { useEffect, useState } from \"react\"; import { useMutation } from \"convex\/react\"; import { api } from \"..\/convex\/_generated\/api\"; import { Id } from \"..\/convex\/_generated\/dataModel\";  export function useStoreUserEffect() {   const { isLoading, isAuthenticated } = useConvexAuth();   const { user } = useUser();   \/\/ \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435   const [userId, setUserId] = useState&lt;Id&lt;\"users\"&gt; | null&gt;(null);   const storeUser = useMutation(api.users.store);   \/\/ \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043c\u0443\u0442\u0430\u0446\u0438\u044e `storeUser` \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f   \/\/ \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 `users` \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430 \u0435\u0433\u043e \u0418\u0414   useEffect(() =&gt; {     \/\/ \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d, \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u043c     if (!isAuthenticated) {       return;     }     \/\/ \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0411\u0414.     \/\/ `storeUser()` \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0447\u0435\u0440\u0435\u0437 \u043e\u0431\u044a\u0435\u043a\u0442 `auth`     \/\/ \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435. \u0412\u0440\u0443\u0447\u043d\u0443\u044e \u043d\u0438\u0447\u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e     async function createUser() {       const id = await storeUser();       setUserId(id);     }     createUser();     return () =&gt; setUserId(null);     \/\/ \u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0445\u0443\u043a \u043f\u0440\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0447\u0435\u0440\u0435\u0437     \/\/ \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440\u0430   }, [isAuthenticated, storeUser, user?.id]);   \/\/ \u041a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u0443\u0435\u043c \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0438\u0437 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430   return {     isLoading: isLoading || (isAuthenticated &amp;&amp; userId === null),     isAuthenticated: isAuthenticated &amp;&amp; userId !== null,   }; }<\/code><\/pre>\n<p> <\/p>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u044d\u0442\u043e\u0442 \u0445\u0443\u043a \u0432 \u0432\u0435\u0440\u0445\u043d\u0435\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u043e\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ src\/App.tsx import { SignInButton, UserButton } from \"@clerk\/clerk-react\"; import { useQuery } from \"convex\/react\"; import { api } from \"..\/convex\/_generated\/api\"; import { useStoreUserEffect } from \".\/useStoreUserEffect.js\";  function App() {   const { isLoading, isAuthenticated } = useStoreUserEffect();    return (     &lt;main&gt;       {isLoading ? (         &lt;&gt;\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430...&lt;\/&gt;       ) : !isAuthenticated ? (         &lt;SignInButton \/&gt;       ) : (         &lt;&gt;           &lt;UserButton \/&gt;           &lt;Content \/&gt;         &lt;\/&gt;       )}     &lt;\/main&gt;   ); }  function Content() {   const messages = useQuery(api.messages.getForCurrentUser);    return &lt;div&gt;\u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442: {messages?.length}&lt;\/div&gt;; }  export default App;<\/code><\/pre>\n<p> <\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 <code>useStoreUserEffect()<\/code> \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 <code>useConvexAuth()<\/code>.<\/p>\n<p> <\/p>\n<p><em>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0418\u0414 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/em><\/p>\n<p> <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0435\u0433\u043e \u0418\u0414 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u0432\u043d\u0435\u0448\u043d\u0438\u0439 \u043a\u043b\u044e\u0447 (foreign key) \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0445:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { v } from \"convex\/values\"; import { mutation } from \".\/_generated\/server\";  export const send = mutation({   args: { body: v.string() },   handler: async (ctx, args) =&gt; {     const identity = await ctx.auth.getUserIdentity();     if (!identity) {       throw new Error(\"Unauthorized\");     }     const user = await ctx.db       .query(\"users\")       .withIndex(\"by_token\", (q) =&gt;         q.eq(\"tokenIdentifier\", identity.tokenIdentifier),       )       .unique();     if (!user) {       throw new Error(\"Unauthorized\");     }     await ctx.db.insert(\"messages\", { body: args.body, user: user._id });     \/\/ ...   }, });<\/code><\/pre>\n<p> <\/p>\n<p><em>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u043e \u0418\u0414<\/em><\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { query } from \".\/_generated\/server\";  export const list = query({   args: {},   handler: async (ctx) =&gt; {     const messages = await ctx.db.query(\"messages\").collect();      return Promise.all(       messages.map(async (message) =&gt; {         \/\/ \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u0432\u0448\u0435\u0433\u043e \u0435\u0433\u043e         \/\/ \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u0438\u043c\u044f \u0432 \u043f\u043e\u043b\u0435 `author`         const user = await ctx.db.get(message.user);         return {           author: user?.name ?? \"\u0410\u043d\u043e\u043d\u0438\u043c\u0443\u0441\",           ...message,         };       }),     );   }, });<\/code><\/pre>\n<p> <\/p>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0432\u0435\u0431-\u0445\u0443\u043a\u0430<\/strong><\/p>\n<p> <\/p>\n<p>\u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Clerk, \u043d\u043e \u0432 Auth0 <a href=\"https:\/\/auth0.com\/docs\/customize\/actions\/actions-overview\">\u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043f\u043e\u0445\u043e\u0436\u0438\u0439<\/a>.<\/p>\n<p> <\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 Clerk \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0431\u044d\u043a Convex \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0435\u0447\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 HTTP \u043f\u0440\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0435\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430.<\/p>\n<p> <\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/get-convex\/convex-demos\/tree\/main\/users-and-clerk-webhooks\">\u041f\u0440\u0438\u043c\u0435\u0440<\/a><\/li>\n<\/ul>\n<p> <\/p>\n<p><em>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u0432\u0435\u0431-\u0445\u0443\u043a\u0430<\/em><\/p>\n<p> <\/p>\n<p>\u041d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Clerk \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 Webhooks \u0438 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u043c &#171;+ Add Endpoint&#187;.<\/p>\n<p> <\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c Endpoint URL \u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>https:\/\/&lt;\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435-\u0434\u0435\u043f\u043b\u043e\u044f&gt;.convex.site\/clerk-users-webhook<\/code> (\u043e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0434\u043e\u043c\u0435\u043d \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 <code>.site<\/code>, \u0430 \u043d\u0435 \u043d\u0430 <code>.cloud<\/code>). \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0434\u0435\u043f\u043b\u043e\u044f \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>.env.local<\/code> \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438\u043b\u0438 \u0432 \u043f\u0430\u043d\u0435\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Convex \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u044c Deployment URL.<\/p>\n<p> <\/p>\n<p>\u0412 Message Filtering \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c user \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439.<\/p>\n<p> <\/p>\n<p>\u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c Create.<\/p>\n<p> <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c Signing Secret, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c\u0441\u044f \u0441 <code>whsec_<\/code>. \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0435\u0433\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f <code>CLERK_WEBHOOK_SECRET<\/code> \u0432 \u043f\u0430\u043d\u0435\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Convex.<\/p>\n<p> <\/p>\n<p><em>\u0421\u0445\u0435\u043c\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 (\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e)<\/em><\/p>\n<p> <\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 <code>\"users\"<\/code>, \u0430 \u0442\u0430\u043a\u0436\u0435, \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e, \u0438\u043d\u0434\u0435\u043a\u0441 \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u044e\u0437\u0435\u0440\u043e\u0432 \u0432 \u0411\u0414:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">users: defineTable({   name: v.string(),   \/\/ \u042d\u0442\u043e Clerk ID, \u0445\u0440\u0430\u043d\u044f\u0449\u0438\u0439\u0441\u044f \u0432 \u043f\u043e\u043b\u0435 `subject` JWT   externalId: v.string(), }).index(\"byExternalId\", [\"externalId\"]),<\/code><\/pre>\n<p> <\/p>\n<p><em>\u041c\u0443\u0442\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0432\u0441\u0442\u0430\u0432\u043a\u0438 \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/em><\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ convex\/users.ts import { internalMutation, query, QueryCtx } from \".\/_generated\/server\"; import { UserJSON } from \"@clerk\/backend\"; import { v, Validator } from \"convex\/values\";  \/\/ \u041f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u044e\u0437\u0435\u0440\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \/\/ \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0432\u0435\u0431-\u0445\u0443\u043a\u0430 export const current = query({   args: {},   handler: async (ctx) =&gt; {     return await getCurrentUser(ctx);   }, });  \/\/ \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u044e\u0437\u0435\u0440\u0430 \u0438\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0435\u0433\u043e \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430 export const upsertFromClerk = internalMutation({   args: { data: v.any() as Validator&lt;UserJSON&gt; }, \/\/ \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043d\u0435\u0442, \u043c\u044b \u0434\u043e\u0432\u0435\u0440\u044f\u0435\u043c Clerk   async handler(ctx, { data }) {     const userAttributes = {       name: `${data.first_name} ${data.last_name}`,       externalId: data.id,     };      const user = await userByExternalId(ctx, data.id);     if (user === null) {       await ctx.db.insert(\"users\", userAttributes);     } else {       await ctx.db.patch(user._id, userAttributes);     }   }, });  \/\/ \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f export const deleteFromClerk = internalMutation({   args: { clerkUserId: v.string() },   async handler(ctx, { clerkUserId }) {     const user = await userByExternalId(ctx, clerkUserId);      if (user !== null) {       await ctx.db.delete(user._id);     } else {       console.warn(         `\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0435\u0433\u043e Clerk ID: ${clerkUserId}`,       );     }   }, });  \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \/\/ \u0438\u043b\u0438 \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 export async function getCurrentUserOrThrow(ctx: QueryCtx) {   const userRecord = await getCurrentUser(ctx);   if (!userRecord) {     throw new Error(\"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442\");   }   return userRecord; }  \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 `null` export async function getCurrentUser(ctx: QueryCtx) {   const identity = await ctx.auth.getUserIdentity();   if (identity === null) {     return null;   }   return await userByExternalId(ctx, identity.subject); }  \/\/ \u0418\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u044e\u0437\u0435\u0440\u0430 \u043f\u043e Clerk ID async function userByExternalId(ctx: QueryCtx, externalId: string) {   return await ctx.db     .query(\"users\")     .withIndex(\"byExternalId\", (q) =&gt; q.eq(\"externalId\", externalId))     .unique(); }<\/code><\/pre>\n<p> <\/p>\n<p><em>\u041a\u043e\u043d\u0435\u0447\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0432\u0435\u0431-\u0445\u0443\u043a\u0430<\/em><\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ convex\/http.ts import { httpRouter } from \"convex\/server\"; import { httpAction } from \".\/_generated\/server\"; import { internal } from \".\/_generated\/api\"; import type { WebhookEvent } from \"@clerk\/backend\"; import { Webhook } from \"svix\";  const http = httpRouter();  http.route({   path: \"\/clerk-users-webhook\",   method: \"POST\",   handler: httpAction(async (ctx, request) =&gt; {     const event = await validateRequest(request);     if (!event) {       return new Response(\"\u0412\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430\", { status: 400 });     }     switch (event.type) {       case \"user.created\": \/\/ \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u043e\u0435 \"\u043f\u0440\u043e\u0432\u0430\u043b\u0438\u0432\u0430\u043d\u0438\u0435\"       case \"user.updated\":         await ctx.runMutation(internal.users.upsertFromClerk, {           data: event.data,         });         break;        case \"user.deleted\": {         const clerkUserId = event.data.id!;         await ctx.runMutation(internal.users.deleteFromClerk, { clerkUserId });         break;       }       default:         console.log(\"\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u043c\u043e\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u0435 \u0432\u0435\u0431-\u0445\u0443\u043a\u0430\", event.type);     }      return new Response(null, { status: 200 });   }), });  async function validateRequest(req: Request): Promise&lt;WebhookEvent | null&gt; {   const payloadString = await req.text();   const svixHeaders = {     \"svix-id\": req.headers.get(\"svix-id\")!,     \"svix-timestamp\": req.headers.get(\"svix-timestamp\")!,     \"svix-signature\": req.headers.get(\"svix-signature\")!,   };   const wh = new Webhook(process.env.CLERK_WEBHOOK_SECRET!);   try {     return wh.verify(payloadString, svixHeaders) as unknown as WebhookEvent;   } catch (error) {     console.error(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0432\u0435\u0431-\u0445\u0443\u043a\u0430\", error);     return null;   } }  export default http;<\/code><\/pre>\n<p> <\/p>\n<p><em>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/em><\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { v } from \"convex\/values\"; import { mutation } from \".\/_generated\/server\"; import { getCurrentUserOrThrow } from \".\/users\";  export const send = mutation({   args: { body: v.string() },   handler: async (ctx, args) =&gt; {     const user = await getCurrentUserOrThrow(ctx);     await ctx.db.insert(\"messages\", { body: args.body, userId: user._id });   }, });<\/code><\/pre>\n<p> <\/p>\n<p><em>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/em><\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">export const list = query({   args: {},   handler: async (ctx) =&gt; {     const messages = await ctx.db.query(\"messages\").collect();     return Promise.all(       messages.map(async (message) =&gt; {         const user = await ctx.db.get(message.user);         return {           author: user?.name ?? \"\u0410\u043d\u043e\u043d\u0438\u043c\u0443\u0441\",           ...message,         };       }),     );   }, });<\/code><\/pre>\n<p> <\/p>\n<p><em>\u041e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/em><\/p>\n<p> <\/p>\n<p>\u0425\u0443\u043a \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u044e\u0437\u0435\u0440\u0430 \u0441 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u043e\u0439 \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u0435\u0433\u043e \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0411\u0414:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">\/\/ src\/useCurrentUser.ts import { useConvexAuth, useQuery } from \"convex\/react\"; import { api } from \"..\/convex\/_generated\/api\";  export function useCurrentUser() {   const { isLoading, isAuthenticated } = useConvexAuth();   const user = useQuery(api.users.current);   \/\/ \u041a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u0443\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u043e\u0439 \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f   return {     isLoading: isLoading || (isAuthenticated &amp;&amp; user === null),     isAuthenticated: isAuthenticated &amp;&amp; user !== null,   }; }<\/code><\/pre>\n<p> <\/p>\n<p>\u0417\u0430\u0442\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b:<\/p>\n<p> <\/p>\n<pre><code class=\"javascript\">import { useCurrentUser } from \".\/useCurrentUser\";  export default function App() {   const { isLoading, isAuthenticated } = useCurrentUser();    return (     &lt;main&gt;       {isLoading ? (         &lt;&gt;\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430...&lt;\/&gt;       ) : isAuthenticated ? (         &lt;Content \/&gt;       ) : (         &lt;LoginPage \/&gt;       )}     &lt;\/main&gt;   ); }<\/code><\/pre>\n<p> <\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430. \u0414\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0438 \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438.<\/p>\n<p> <\/p>\n<hr\/>\n<p> <\/p>\n<blockquote><p><a href=\"https:\/\/t.me\/timewebru\"><b>\u041d\u043e\u0432\u043e\u0441\u0442\u0438, \u043e\u0431\u0437\u043e\u0440\u044b \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432 \u0438 \u043a\u043e\u043d\u043a\u0443\u0440\u0441\u044b \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b Timeweb.Cloud \u2014 \u0432 \u043d\u0430\u0448\u0435\u043c Telegram-\u043a\u0430\u043d\u0430\u043b\u0435<\/b><\/a> <b>\u21a9<\/b><\/p><\/blockquote>\n<p><a href=\"https:\/\/timeweb.cloud\/?i=106909\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/u9\/vg\/io\/u9vgio3hxj12h5u7j3un0wx_zpk.png\" data-src=\"https:\/\/habrastorage.org\/webt\/u9\/vg\/io\/u9vgio3hxj12h5u7j3un0wx_zpk.png\"\/><\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/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\/articles\/851244\/\"> https:\/\/habr.com\/ru\/articles\/851244\/<\/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-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/lq\/qm\/e7\/lqqme7wqqxhte55gsiz94gwwh4u.png\" data-src=\"https:\/\/habrastorage.org\/webt\/lq\/qm\/e7\/lqqme7wqqxhte55gsiz94gwwh4u.png\"\/> <\/p>\n<p> \u041f\u0440\u0438\u0432\u0435\u0442, \u0434\u0440\u0443\u0437\u044c\u044f!<\/p>\n<p> <\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0435\u0440\u0438\u0438 \u0441\u0442\u0430\u0442\u0435\u0439 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e \u043e <a href=\"https:\/\/www.convex.dev\/\">Convex<\/a> \u2014 \u043d\u043e\u0432\u043e\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u043c \u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0438 BaaS (Backend as a Service \u2014 \u0431\u044d\u043a\u0435\u043d\u0434 \u043a\u0430\u043a \u0443\u0441\u043b\u0443\u0433\u0430), \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e\u043e\u0431\u0435\u0449\u0430\u044e\u0449\u0435 \u0438 \u0431\u044b\u0441\u0442\u0440\u043e \u043d\u0430\u0431\u0438\u0440\u0430\u0435\u0442 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0441\u0442\u044c \u0441\u0440\u0435\u0434\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432.<\/p>\n<p> <\/p>\n<p>\u041d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0438\u0439 \u0434\u0435\u043d\u044c Convex \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u043c\u0435\u0448\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430, \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438\/\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u0444\u0430\u0439\u043b\u043e\u0432\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a \u0437\u0430\u0434\u0430\u0447 \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430.<\/p>\n<p> <\/p>\n<p>\u042d\u0442\u0430 \u0432\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0441\u0435\u0440\u0438\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e\u0431 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p> <\/p>\n<ul>\n<li><a href=\"https:\/\/habr.com\/ru\/companies\/timeweb\/articles\/844878\/\">\u041f\u0435\u0440\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c<\/a><\/li>\n<\/ul>\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-443465","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/443465","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=443465"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/443465\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=443465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=443465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=443465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}