{"action":"created","comment":{"url":"https://api.github.com/repos/doomspork/elixir-school/pulls/comments/93382194","pull_request_review_id":13907374,"id":93382194,"diff_hunk":"@@ -0,0 +1,249 @@\n+---\n+layout: page\n+title: Guardian (Основы)\n+category: libraries\n+order: 1\n+lang: ru\n+---\n+\n+[Guardian](https://github.com/ueberauth/guardian) &mdash; часто используемая библиотека, основанная на [JWT](https://jwt.io/) (Javascript Web Token).\n+\n+{% include toc.html %}\n+\n+## JWT\n+\n+Что такое JWT? JWT занимается созданием токена для аутентификации. В то время как многие системы аутентификации предоставляют доступ только к идентификатору ресурса, JWT позволяет хранить еще и другую информацию, такую как:\n+\n+* Кем выдан токен\n+* Для кого выдан токен\n+* Какие системы должны использовать этот токен\n+* На какое время он выдан\n+* Каков тип токена\n+* Какие права есть у владельца данного токена\n+\n+Это всё &mdash; простые примеры полей в JWT. Технология позволяет добавлять любую дополнительную информацию, которой требует логика приложения. Главное &mdash; не забывайте что этот токен должен быть коротким, ведь он должен поместиться в HTTP заголовке.\n+\n+Следствием такого подхода является тот факт, что JWT могут передаваться как полноценный целостный элемент данных идентификации пользователя.\n+\n+### Когда их использовать\n+\n+Токены JWT могут использваться для аутентификации в любой части приложения:\n+\n+* Одностраничные приложения\n+* Контроллеры (как с помощью браузерной сессии, так и api: с помощью HTTP заголовков)\n+* Phoenix Channels\n+* Запросы между сервисами\n+* Запросы между процессами\n+* Функциональность \"запомнить меня\"\n+\n+Такие токены могут использоваться в приложении где угодно, когда нужно предоставить проверяемую аутентификцию.\n+\n+### Нужно ли использовать базу данных?\n+\n+JWT токены не нужно отслеживать с помощью базы данных. Достаточно знать, что\n+ они уже выписаны и использовать время жизни токена для контроля доступа. Часто встречаются случаи, когда нужно сделать запрос в базу для поиска/получения нужного ресурса, однако JWT это не требует.\n+\n+Например, если нужно аутентифицировать подключение к UDP сокету с помощью JWT, подключение к базе данных скорее всего не понадобится. Вся необходимая информация уже будет встроена в токен в момент его создания. Данные из токена можно использовать сразу после проверки подписи.\n+\n+Однако, _можно_ использовать базу данных для отслеживания JWT токенов. Если воспользоваться этой возможностью, можно проверить что токен не был отозван. Как вариант, можно использовать записи в базе данных для отзыва сразу всех токенов пользователя. Это довольно просто реализуется в Guardian с помощью [GuardianDb](https://github.com/hassox/guardian_db). GuardianDb использует интерфейсы Guardian для проведения валидации, сохранения и удаления из базы данных. Мы дойдем до этой темы позже.\n+\n+## Настройка\n+\n+В Guardian довольно много различных настроек. Мы рассмотрим их позже, но начнем с самого простого варианта.\n+\n+### Минимальная настройка\n+\n+Для начала работы с Guardian нужно сделать несколько вещей.\n+\n+#### Настройка \n+\n+`mix.exs`\n+\n+```elixir\n+def application do\n+  [\n+    mod: {MyApp, []},\n+    applications: [:guardian, ...]\n+  ]\n+end\n+\n+def deps do\n+  [\n+    {guardian: \"~> x.x\"},\n+    ...\n+  ]\n+end\n+```\n+\n+`config/config.ex`\n+\n+```elixir\n+config :guardian, Guardian,\n+  issuer: \"MyAppId\",\n+  secret_key: Mix.env, # в каждом окружении стоит переопределять этот ключ\n+  serializer: MyApp.GuardianSerializer\n+```\n+\n+Это минимальный набор информации, который нужно предоставить Guardian для функционирования. Не стоит встраивать секретный ключ напрямую в основную конфигурацию приложения. Вместо этого у каждого окружения должен быть свой ключ. Хорошей практикой является использовать название Mix окружения в качестве ключа для dev и test окружений. В то же время, staging и production окружения должны использовать сложный секретный ключ (например, сгенерированный с помощью `mix phoenix.gen.secret`).\n+\n+`lib/my_app/guardian_serializer.ex`\n+\n+```elixir\n+defmodule MyApp.GuardianSerializer do\n+  @behaviour Guardian.Serializer\n+\n+  alias MyApp.Repo\n+  alias MyApp.User\n+\n+  def for_token(user = %User{}), do: { :ok, \"User:#{user.id}\" }\n+  def for_token(_), do: { :error, \"Неизвестный ресурс\" }\n+\n+  def from_token(\"User:\" <> id), do: { :ok, Repo.get(User, id) }\n+  def from_token(_), do: { :error, \"Неизвестный ресурс\" }\n+end\n+```\n+\n+Сериализатор отвечает за определение нужного ресурса на основе данных из токена. Это может быть поиск в базе данных, внешнем API &mdash; или даже простая строка. Он также отвечает за обратный процесс превращения объекта пользователя в сериализованный вариант.\n+\n+Это всё, что нужно для минимальной настройки. Есть еще много других настроек, но для начала этого достаточно.\n+\n+#### Использование в приложении\n+\n+Теперь, когда вся конфигурация настроена, нам нужно интегрировать сам Guardian в приложение. Так как это минималистичный вариант, давайте сначала рассмотрим HTTP запросы.\n+\n+## HTTP запросы\n+\n+Guardian предоставляет различные Plug для интеграции в HTTP запросы. О Plug можно почитать в [другом уроке](../specifics/plug/). Guardian не требует Phoenix, с ним легче всего будет привести следующие примеры.\n+\n+Самый легкий способ интеграции &mdash; роутер. Так как HTTP интеграции Guardian основаны на plug-ах, их можно использовать в любом совместимом инструментарии.\n+\n+Основная логика Guardian plug выглядит так:\n+\n+1. Найти токен внутри запроса и проверить его: `Verify...` plug-и.\n+2. Опционально загрузить ресурсы, которые указаны в токене: plug `LoadResource`.\n+3. Проверить, что найден пользователь. Иначе &mdash; запретить доступ: plug `EnsureAuthenticated`.\n+\n+Для большей гибкости, Guardian реализует все эти фазы отдельными plug-ами. \n+\n+Давайте воспользуемся этими знаниями:\n+\n+```elixir\n+pipeline :maybe_browser_auth do\n+  plug Guardian.Plug.VerifySession\n+  plug Guardian.Plug.VerifyHeader, realm: \"Bearer\"\n+  plug Guardian.Plug.LoadResource\n+end\n+\n+pipeline :ensure_authed_access do\n+  plug Guardian.Plug.EnsureAuthenticated, %{\"typ\" => \"access\", handler: MyApp.HttpErrorHandler}\n+end\n+```\n+\n+Эти pipeline могут быть использованы для того, чтобы собрать из них реализацию различных требований к аутентификации. Первый вариант сначала пытается найти токен в сессии, потом &mdash; в заголовках запроса, и, если находит, загружает ресурс.","path":"ru/lessons/libraries/guardian.md","position":142,"original_position":142,"commit_id":"4d447a5e44df0507e13d7a7075ac0075f51de119","original_commit_id":"4d447a5e44df0507e13d7a7075ac0075f51de119","user":{"login":"nscyclone","id":5345029,"avatar_url":"https://avatars.githubusercontent.com/u/5345029?v=3","gravatar_id":"","url":"https://api.github.com/users/nscyclone","html_url":"https://github.com/nscyclone","followers_url":"https://api.github.com/users/nscyclone/followers","following_url":"https://api.github.com/users/nscyclone/following{/other_user}","gists_url":"https://api.github.com/users/nscyclone/gists{/gist_id}","starred_url":"https://api.github.com/users/nscyclone/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nscyclone/subscriptions","organizations_url":"https://api.github.com/users/nscyclone/orgs","repos_url":"https://api.github.com/users/nscyclone/repos","events_url":"https://api.github.com/users/nscyclone/events{/privacy}","received_events_url":"https://api.github.com/users/nscyclone/received_events","type":"User","site_admin":false},"body":"`для того` can be safely removed here.","created_at":"2016-12-21T06:37:58Z","updated_at":"2016-12-21T06:38:50Z","html_url":"https://github.com/doomspork/elixir-school/pull/885#discussion_r93382194","pull_request_url":"https://api.github.com/repos/doomspork/elixir-school/pulls/885","_links":{"self":{"href":"https://api.github.com/repos/doomspork/elixir-school/pulls/comments/93382194"},"html":{"href":"https://github.com/doomspork/elixir-school/pull/885#discussion_r93382194"},"pull_request":{"href":"https://api.github.com/repos/doomspork/elixir-school/pulls/885"}}},"pull_request":{"url":"https://api.github.com/repos/doomspork/elixir-school/pulls/885","id":98495024,"html_url":"https://github.com/doomspork/elixir-school/pull/885","diff_url":"https://github.com/doomspork/elixir-school/pull/885.diff","patch_url":"https://github.com/doomspork/elixir-school/pull/885.patch","issue_url":"https://api.github.com/repos/doomspork/elixir-school/issues/885","number":885,"state":"open","locked":false,"title":"[RU] Add translation for Guardian lesson","user":{"login":"brain-geek","id":307982,"avatar_url":"https://avatars.githubusercontent.com/u/307982?v=3","gravatar_id":"","url":"https://api.github.com/users/brain-geek","html_url":"https://github.com/brain-geek","followers_url":"https://api.github.com/users/brain-geek/followers","following_url":"https://api.github.com/users/brain-geek/following{/other_user}","gists_url":"https://api.github.com/users/brain-geek/gists{/gist_id}","starred_url":"https://api.github.com/users/brain-geek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brain-geek/subscriptions","organizations_url":"https://api.github.com/users/brain-geek/orgs","repos_url":"https://api.github.com/users/brain-geek/repos","events_url":"https://api.github.com/users/brain-geek/events{/privacy}","received_events_url":"https://api.github.com/users/brain-geek/received_events","type":"User","site_admin":false},"body":"","created_at":"2016-12-19T01:05:49Z","updated_at":"2016-12-21T06:38:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"de338a863ed9b15757a797cf3c241e4ee6f587c8","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/doomspork/elixir-school/pulls/885/commits","review_comments_url":"https://api.github.com/repos/doomspork/elixir-school/pulls/885/comments","review_comment_url":"https://api.github.com/repos/doomspork/elixir-school/pulls/comments{/number}","comments_url":"https://api.github.com/repos/doomspork/elixir-school/issues/885/comments","statuses_url":"https://api.github.com/repos/doomspork/elixir-school/statuses/4d447a5e44df0507e13d7a7075ac0075f51de119","head":{"label":"brain-geek:ru_guardian","ref":"ru_guardian","sha":"4d447a5e44df0507e13d7a7075ac0075f51de119","user":{"login":"brain-geek","id":307982,"avatar_url":"https://avatars.githubusercontent.com/u/307982?v=3","gravatar_id":"","url":"https://api.github.com/users/brain-geek","html_url":"https://github.com/brain-geek","followers_url":"https://api.github.com/users/brain-geek/followers","following_url":"https://api.github.com/users/brain-geek/following{/other_user}","gists_url":"https://api.github.com/users/brain-geek/gists{/gist_id}","starred_url":"https://api.github.com/users/brain-geek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brain-geek/subscriptions","organizations_url":"https://api.github.com/users/brain-geek/orgs","repos_url":"https://api.github.com/users/brain-geek/repos","events_url":"https://api.github.com/users/brain-geek/events{/privacy}","received_events_url":"https://api.github.com/users/brain-geek/received_events","type":"User","site_admin":false},"repo":{"id":50012963,"name":"elixir-school","full_name":"brain-geek/elixir-school","owner":{"login":"brain-geek","id":307982,"avatar_url":"https://avatars.githubusercontent.com/u/307982?v=3","gravatar_id":"","url":"https://api.github.com/users/brain-geek","html_url":"https://github.com/brain-geek","followers_url":"https://api.github.com/users/brain-geek/followers","following_url":"https://api.github.com/users/brain-geek/following{/other_user}","gists_url":"https://api.github.com/users/brain-geek/gists{/gist_id}","starred_url":"https://api.github.com/users/brain-geek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brain-geek/subscriptions","organizations_url":"https://api.github.com/users/brain-geek/orgs","repos_url":"https://api.github.com/users/brain-geek/repos","events_url":"https://api.github.com/users/brain-geek/events{/privacy}","received_events_url":"https://api.github.com/users/brain-geek/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/brain-geek/elixir-school","description":"Lessons in Elixir programming","fork":true,"url":"https://api.github.com/repos/brain-geek/elixir-school","forks_url":"https://api.github.com/repos/brain-geek/elixir-school/forks","keys_url":"https://api.github.com/repos/brain-geek/elixir-school/keys{/key_id}","collaborators_url":"https://api.github.com/repos/brain-geek/elixir-school/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/brain-geek/elixir-school/teams","hooks_url":"https://api.github.com/repos/brain-geek/elixir-school/hooks","issue_events_url":"https://api.github.com/repos/brain-geek/elixir-school/issues/events{/number}","events_url":"https://api.github.com/repos/brain-geek/elixir-school/events","assignees_url":"https://api.github.com/repos/brain-geek/elixir-school/assignees{/user}","branches_url":"https://api.github.com/repos/brain-geek/elixir-school/branches{/branch}","tags_url":"https://api.github.com/repos/brain-geek/elixir-school/tags","blobs_url":"https://api.github.com/repos/brain-geek/elixir-school/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/brain-geek/elixir-school/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/brain-geek/elixir-school/git/refs{/sha}","trees_url":"https://api.github.com/repos/brain-geek/elixir-school/git/trees{/sha}","statuses_url":"https://api.github.com/repos/brain-geek/elixir-school/statuses/{sha}","languages_url":"https://api.github.com/repos/brain-geek/elixir-school/languages","stargazers_url":"https://api.github.com/repos/brain-geek/elixir-school/stargazers","contributors_url":"https://api.github.com/repos/brain-geek/elixir-school/contributors","subscribers_url":"https://api.github.com/repos/brain-geek/elixir-school/subscribers","subscription_url":"https://api.github.com/repos/brain-geek/elixir-school/subscription","commits_url":"https://api.github.com/repos/brain-geek/elixir-school/commits{/sha}","git_commits_url":"https://api.github.com/repos/brain-geek/elixir-school/git/commits{/sha}","comments_url":"https://api.github.com/repos/brain-geek/elixir-school/comments{/number}","issue_comment_url":"https://api.github.com/repos/brain-geek/elixir-school/issues/comments{/number}","contents_url":"https://api.github.com/repos/brain-geek/elixir-school/contents/{+path}","compare_url":"https://api.github.com/repos/brain-geek/elixir-school/compare/{base}...{head}","merges_url":"https://api.github.com/repos/brain-geek/elixir-school/merges","archive_url":"https://api.github.com/repos/brain-geek/elixir-school/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/brain-geek/elixir-school/downloads","issues_url":"https://api.github.com/repos/brain-geek/elixir-school/issues{/number}","pulls_url":"https://api.github.com/repos/brain-geek/elixir-school/pulls{/number}","milestones_url":"https://api.github.com/repos/brain-geek/elixir-school/milestones{/number}","notifications_url":"https://api.github.com/repos/brain-geek/elixir-school/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/brain-geek/elixir-school/labels{/name}","releases_url":"https://api.github.com/repos/brain-geek/elixir-school/releases{/id}","deployments_url":"https://api.github.com/repos/brain-geek/elixir-school/deployments","created_at":"2016-01-20T07:33:22Z","updated_at":"2016-01-20T07:33:23Z","pushed_at":"2016-12-20T22:41:48Z","git_url":"git://github.com/brain-geek/elixir-school.git","ssh_url":"git@github.com:brain-geek/elixir-school.git","clone_url":"https://github.com/brain-geek/elixir-school.git","svn_url":"https://github.com/brain-geek/elixir-school","homepage":"http://elixirschool.com","size":3430,"stargazers_count":0,"watchers_count":0,"language":"CSS","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"doomspork:master","ref":"master","sha":"7783d9b9c25512c5cfadc1785f3d9774597be5b6","user":{"login":"doomspork","id":73386,"avatar_url":"https://avatars.githubusercontent.com/u/73386?v=3","gravatar_id":"","url":"https://api.github.com/users/doomspork","html_url":"https://github.com/doomspork","followers_url":"https://api.github.com/users/doomspork/followers","following_url":"https://api.github.com/users/doomspork/following{/other_user}","gists_url":"https://api.github.com/users/doomspork/gists{/gist_id}","starred_url":"https://api.github.com/users/doomspork/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/doomspork/subscriptions","organizations_url":"https://api.github.com/users/doomspork/orgs","repos_url":"https://api.github.com/users/doomspork/repos","events_url":"https://api.github.com/users/doomspork/events{/privacy}","received_events_url":"https://api.github.com/users/doomspork/received_events","type":"User","site_admin":false},"repo":{"id":36616176,"name":"elixir-school","full_name":"doomspork/elixir-school","owner":{"login":"doomspork","id":73386,"avatar_url":"https://avatars.githubusercontent.com/u/73386?v=3","gravatar_id":"","url":"https://api.github.com/users/doomspork","html_url":"https://github.com/doomspork","followers_url":"https://api.github.com/users/doomspork/followers","following_url":"https://api.github.com/users/doomspork/following{/other_user}","gists_url":"https://api.github.com/users/doomspork/gists{/gist_id}","starred_url":"https://api.github.com/users/doomspork/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/doomspork/subscriptions","organizations_url":"https://api.github.com/users/doomspork/orgs","repos_url":"https://api.github.com/users/doomspork/repos","events_url":"https://api.github.com/users/doomspork/events{/privacy}","received_events_url":"https://api.github.com/users/doomspork/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/doomspork/elixir-school","description":"Lessons about the Elixir programming language","fork":false,"url":"https://api.github.com/repos/doomspork/elixir-school","forks_url":"https://api.github.com/repos/doomspork/elixir-school/forks","keys_url":"https://api.github.com/repos/doomspork/elixir-school/keys{/key_id}","collaborators_url":"https://api.github.com/repos/doomspork/elixir-school/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/doomspork/elixir-school/teams","hooks_url":"https://api.github.com/repos/doomspork/elixir-school/hooks","issue_events_url":"https://api.github.com/repos/doomspork/elixir-school/issues/events{/number}","events_url":"https://api.github.com/repos/doomspork/elixir-school/events","assignees_url":"https://api.github.com/repos/doomspork/elixir-school/assignees{/user}","branches_url":"https://api.github.com/repos/doomspork/elixir-school/branches{/branch}","tags_url":"https://api.github.com/repos/doomspork/elixir-school/tags","blobs_url":"https://api.github.com/repos/doomspork/elixir-school/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/doomspork/elixir-school/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/doomspork/elixir-school/git/refs{/sha}","trees_url":"https://api.github.com/repos/doomspork/elixir-school/git/trees{/sha}","statuses_url":"https://api.github.com/repos/doomspork/elixir-school/statuses/{sha}","languages_url":"https://api.github.com/repos/doomspork/elixir-school/languages","stargazers_url":"https://api.github.com/repos/doomspork/elixir-school/stargazers","contributors_url":"https://api.github.com/repos/doomspork/elixir-school/contributors","subscribers_url":"https://api.github.com/repos/doomspork/elixir-school/subscribers","subscription_url":"https://api.github.com/repos/doomspork/elixir-school/subscription","commits_url":"https://api.github.com/repos/doomspork/elixir-school/commits{/sha}","git_commits_url":"https://api.github.com/repos/doomspork/elixir-school/git/commits{/sha}","comments_url":"https://api.github.com/repos/doomspork/elixir-school/comments{/number}","issue_comment_url":"https://api.github.com/repos/doomspork/elixir-school/issues/comments{/number}","contents_url":"https://api.github.com/repos/doomspork/elixir-school/contents/{+path}","compare_url":"https://api.github.com/repos/doomspork/elixir-school/compare/{base}...{head}","merges_url":"https://api.github.com/repos/doomspork/elixir-school/merges","archive_url":"https://api.github.com/repos/doomspork/elixir-school/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/doomspork/elixir-school/downloads","issues_url":"https://api.github.com/repos/doomspork/elixir-school/issues{/number}","pulls_url":"https://api.github.com/repos/doomspork/elixir-school/pulls{/number}","milestones_url":"https://api.github.com/repos/doomspork/elixir-school/milestones{/number}","notifications_url":"https://api.github.com/repos/doomspork/elixir-school/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/doomspork/elixir-school/labels{/name}","releases_url":"https://api.github.com/repos/doomspork/elixir-school/releases{/id}","deployments_url":"https://api.github.com/repos/doomspork/elixir-school/deployments","created_at":"2015-05-31T18:27:42Z","updated_at":"2016-12-20T20:22:26Z","pushed_at":"2016-12-20T22:41:49Z","git_url":"git://github.com/doomspork/elixir-school.git","ssh_url":"git@github.com:doomspork/elixir-school.git","clone_url":"https://github.com/doomspork/elixir-school.git","svn_url":"https://github.com/doomspork/elixir-school","homepage":"https://elixirschool.com","size":3443,"stargazers_count":790,"watchers_count":790,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":281,"mirror_url":null,"open_issues_count":87,"forks":281,"open_issues":87,"watchers":790,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/doomspork/elixir-school/pulls/885"},"html":{"href":"https://github.com/doomspork/elixir-school/pull/885"},"issue":{"href":"https://api.github.com/repos/doomspork/elixir-school/issues/885"},"comments":{"href":"https://api.github.com/repos/doomspork/elixir-school/issues/885/comments"},"review_comments":{"href":"https://api.github.com/repos/doomspork/elixir-school/pulls/885/comments"},"review_comment":{"href":"https://api.github.com/repos/doomspork/elixir-school/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/doomspork/elixir-school/pulls/885/commits"},"statuses":{"href":"https://api.github.com/repos/doomspork/elixir-school/statuses/4d447a5e44df0507e13d7a7075ac0075f51de119"}}}}
{"actor":{"display_login":"nscyclone"}}