Введение
В этом руководстве показано, как создать кнопку "Вход с помощью GitHub" для веб-сайта. Веб-сайт будет использовать GitHub App для создания маркера доступа пользователей через поток веб-приложения. Затем веб-сайт использует маркер доступа пользователя для выполнения запросов API от имени прошедшего проверку подлинности пользователя.
В этом руководстве используется Ruby, но вы можете использовать поток веб-приложения с любым языком программирования, используемым для веб-разработки.
Сведения о потоке веб-приложения и маркерах доступа пользователей
Приложение должно использовать маркер доступа пользователя, если вы хотите атрибутировать действия приложения пользователю. Дополнительные сведения см. в разделе Проверка подлинности с помощью приложения GitHub от имени пользователя.
Существует два способа создания маркера доступа пользователя для GitHub App: поток веб-приложения и поток устройств. Если у вашего приложения есть доступ к веб-интерфейсу, следует использовать поток веб-приложения. Если приложение не имеет доступа к веб-интерфейсу, вместо этого следует использовать поток устройств. Дополнительные сведения см. в разделе "[AUTOTITLE" и "Создание маркера доступа пользователя для приложения GitHub](/apps/creating-github-apps/guides/building-a-cli-with-a-github-app)".
Необходимые компоненты
В этом руководстве предполагается, что вы уже зарегистрировали GitHub App. Дополнительные сведения о регистрации GitHub Appсм. в разделе "Регистрация приложения GitHub".
Перед выполнением этого руководства необходимо задать URL-адрес обратного вызова для приложения. В этом руководстве используется локальный сервер Sinatra с URL-адресом http://localhost:4567
по умолчанию. Например, для работы с URL-адресом по умолчанию для локального приложения Sinatra может быть http://localhost:4567/github/callback
URL-адрес обратного вызова. Когда вы будете готовы к развертыванию приложения, вы можете изменить URL-адрес обратного вызова, чтобы использовать адрес динамического сервера. Дополнительные сведения об обновлении URL-адреса обратного вызова для приложения см. в разделе "[AUTOTITLE" и "Изменение регистрации приложения GitHub](/apps/creating-github-apps/setting-up-a-github-app/about-the-user-authorization-callback-url)".
В этом руководстве предполагается, что у вас есть базовое представление о Ruby и системе шаблонов Ruby, ERB. Дополнительные сведения см. в разделе Ruby и ERB.
Установка зависимостей
В этом руководстве используется драгоценный камень Ruby, Sinatra, чтобы создать веб-приложение с помощью Ruby. Дополнительные сведения см. в Sinatra README.
В этом руководстве используется драгоценный камень Ruby, dotenv, для доступа к значениям, хранящимся в .env
файле. Дополнительные сведения см . в dotenv README.
Чтобы следовать этому руководству, необходимо установить драгоценные камни Sinatra и dotenv в проекте Ruby. Например, это можно сделать с помощью bundler:
-
Если у вас еще нет пакета, выполните следующую команду в терминале:
gem install bundler
-
Если у вас еще нет файла Gemfile для приложения, выполните следующую команду в терминале:
bundle init
-
Если у вас еще нет файла Gemfile.lock для приложения, выполните следующую команду в терминале:
bundle install
-
Установите драгоценные камни, выполнив следующие команды в терминале:
bundle add sinatra
bundle add dotenv
Хранение идентификатора клиента и секрета клиента
В этом руководстве показано, как сохранить идентификатор клиента и секрет клиента в переменных среды и получить к ним ENV.fetch
доступ. При развертывании приложения необходимо изменить способ хранения идентификатора клиента и секрета клиента. Дополнительные сведения см. в разделе "Безопасное хранение секрета клиента".
-
В правом верхнем углу любой страницы на GitHubщелкните фото профиля.
-
Перейдите к настройкам учетной записи.
- Для приложения, принадлежащих личная учетная запись, нажмите кнопку "Параметры".
- Для приложения, принадлежащих организации:
- Щелкните Your organizations (Ваши организации).
- Справа от организации нажмите кнопку "Параметры".
-
На левой боковой панели щелкните Параметры разработчика.
-
На левой боковой панели щелкните GitHub Apps.
-
Рядом с GitHub App, с которыми вы хотите работать, нажмите кнопку "Изменить".
-
На странице параметров приложения найдите идентификатор клиента для приложения. Вы добавите его в
.env
файл на следующем шаге. Обратите внимание, что идентификатор клиента отличается от идентификатора приложения. -
На странице параметров приложения нажмите кнопку "Создать новый секрет клиента". Вы добавите секрет клиента в
.env
файл на следующем шаге. -
Создайте файл, который вызывается
.env
на том же уровне, что и вашGemfile
. -
Если у проекта еще нет
.gitignore
файла, создайте.gitignore
файл на том же уровне, что и вашGemfile
. -
Добавьте
.env
в.gitignore
файл. Это позволит предотвратить случайное фиксацию секрета клиента. Дополнительные сведения о файлах см. в.gitignore
разделе "Пропуск файлов". -
Добавьте в файл следующее содержимое
.env
. ЗаменитеYOUR_CLIENT_ID
идентификатором клиента приложения. ЗаменитеYOUR_CLIENT_SECRET
секрет клиента для приложения.CLIENT_ID="YOUR_CLIENT_ID" CLIENT_SECRET="YOUR_CLIENT_SECRET"
Добавление кода для создания маркера доступа пользователя
Чтобы получить маркер доступа пользователя, сначала необходимо предложить пользователю авторизовать приложение. Когда пользователь авторизует приложение, он перенаправляется по URL-адресу обратного вызова приложения. Запрос к URL-адресу обратного вызова включает code
параметр запроса. Когда приложение получает запрос на обслуживание этого URL-адреса обратного вызова, можно обменять code
параметр на маркер доступа пользователя.
Эти шаги приводят к написанию кода для создания маркера доступа пользователей. Сведения о переходе к окончательному коду см. в разделе "Полный пример кода".
-
В том же каталоге, что
.env
и файл, создайте файл Ruby для хранения кода, который создаст маркер доступа пользователя. В этом руководстве будет присвоено имя файлаapp.rb
. -
В верхней части
app.rb
добавьте следующие зависимости:Ruby require "sinatra" require "dotenv/load" require "net/http" require "json"
require "sinatra" require "dotenv/load" require "net/http" require "json"
dotenv/load
Иsinatra
зависимости используют установленные ранее драгоценные камни.net/http
иjson
являются частью стандартной библиотеки Ruby. -
Добавьте следующий код
app.rb
, чтобы получить идентификатор клиента и секрет клиента приложения из.env
файла.Ruby CLIENT_ID = ENV.fetch("CLIENT_ID") CLIENT_SECRET = ENV.fetch("CLIENT_SECRET")
CLIENT_ID = ENV.fetch("CLIENT_ID") CLIENT_SECRET = ENV.fetch("CLIENT_SECRET")
-
Добавьте следующий код, чтобы
app.rb
отобразить ссылку, которая предложит пользователям пройти проверку подлинности приложения.Ruby get "/" do link = '<a href="https://github.com/login/oauth/authorize?client_id=<%= CLIENT_ID %>">Login with GitHub</a>' erb link end
get "/" do link = '<a href="https://github.com/login/oauth/authorize?client_id=<%= CLIENT_ID %>">Login with GitHub</a>' erb link end
-
Добавьте следующий код для
app.rb
обработки запросов к URL-адресу обратного вызова приложения и полученияcode
параметра из запроса. ЗаменитеCALLBACK_URL
URL-адрес обратного вызова для приложения, минус домен. Например, если URL-адрес обратного вызова имеет значениеhttp://localhost:4567/github/callback
, заменитеCALLBACK_URL
на/github/callback
.Ruby get "CALLBACK_URL" do code = params["code"] render = "Successfully authorized! Got code #{code}." erb render end
get "CALLBACK_URL" do code = params["code"] render = "Successfully authorized! Got code #{code}." erb render end
В настоящее время код просто отображает сообщение вместе с параметром
code
. Ниже описано, как развернуть этот блок кода. -
При необходимости проверьте ход выполнения:
app.rb
Теперь выглядит следующим образом, гдеCALLBACK_URL
находится URL-адрес обратного вызова для приложения, минус домен:Ruby require "sinatra" require "dotenv/load" require "net/http" require "json" CLIENT_ID = ENV.fetch("CLIENT_ID") CLIENT_SECRET = ENV.fetch("CLIENT_SECRET") get "/" do link = '<a href="https://github.com/login/oauth/authorize?client_id=<%= CLIENT_ID %>">Login with GitHub</a>' erb link end get "CALLBACK_URL" do code = params["code"] render = "Successfully authorized! Got code #{code}." erb render end
require "sinatra" require "dotenv/load" require "net/http" require "json" CLIENT_ID = ENV.fetch("CLIENT_ID") CLIENT_SECRET = ENV.fetch("CLIENT_SECRET") get "/" do link = '<a href="https://github.com/login/oauth/authorize?client_id=<%= CLIENT_ID %>">Login with GitHub</a>' erb link end get "CALLBACK_URL" do code = params["code"] render = "Successfully authorized! Got code #{code}." erb render end
-
В терминале в каталоге, где
app.rb
хранится, выполните командуruby app.rb
. Локальный сервер Sinatra должен запускаться. -
В браузере перейдите по адресу
http://localhost:4567
. Вы увидите ссылку с текстом "Вход с помощью GitHub". -
Щелкните ссылку "Вход с помощью GitHub".
Если вы не авторизовано приложение, щелкните ссылку,
https://github.com/login/oauth/authorize?client_id=CLIENT_ID
в которойCLIENT_ID
находится идентификатор клиента вашего приложения. Это страница GitHub, которая предлагает пользователям авторизовать приложение. Если нажать кнопку, чтобы авторизовать приложение, вы перейдете по URL-адресу обратного вызова для приложения.Если вы ранее авторизовано приложение и авторизация не были отменены, вы пропустите запрос авторизации и перейдите непосредственно к URL-адресу обратного вызова. Вы можете отозвать предыдущую авторизацию, если вы хотите просмотреть запрос на авторизацию. Дополнительные сведения см. в разделе Проверка и отзыв авторизации приложений GitHub.
-
Страница URL-адреса обратного вызова, достигнутная путем нажатия ссылки "Вход с помощью GitHub", а затем авторизация приложения при появлении запроса на это, должна отображать текст, аналогичный "Успешно авторизовано! Получил код agc622abb6135be5d1f2".
-
В терминале, где работает Sinatra, остановите сервер, введя ctrl+C.
-
-
Замените содержимое следующим кодом, где
CALLBACK_URL
находится URL-адрес обратногоapp.rb
вызова приложения, минус домен.Этот код добавляет логику для обмена
code
параметром маркера доступа пользователя:- Функция
parse_response
анализирует ответ из API GitHub. - Функция
exchange_code
обмениваетсяcode
параметром маркера доступа пользователя. - Обработчик запроса обратного вызова теперь вызывает
exchange_code
параметр кода для маркера доступа пользователя. - Теперь на странице обратного вызова отображается текст, указывающий, что маркер был создан. Если создание токена не выполнено успешно, страница будет указывать на это сбой.
Ruby require "sinatra" require "dotenv/load" require "net/http" require "json" CLIENT_ID = ENV.fetch("CLIENT_ID") CLIENT_SECRET = ENV.fetch("CLIENT_SECRET") def parse_response(response) case response when Net::HTTPOK JSON.parse(response.body) else puts response puts response.body {} end end def exchange_code(code) params = { "client_id" => CLIENT_ID, "client_secret" => CLIENT_SECRET, "code" => code } result = Net::HTTP.post( URI("https://github.com/login/oauth/access_token"), URI.encode_www_form(params), {"Accept" => "application/json"} ) parse_response(result) end get "/" do link = '<a href="https://github.com/login/oauth/authorize?client_id=<%= CLIENT_ID %>">Login with GitHub</a>' erb link end get "CALLBACK_URL" do code = params["code"] token_data = exchange_code(code) if token_data.key?("access_token") token = token_data["access_token"] render = "Successfully authorized! Got code #{code} and exchanged it for a user access token ending in #{token[-9..-1]}." erb render else render = "Authorized, but unable to exchange code #{code} for token." erb render end end
require "sinatra" require "dotenv/load" require "net/http" require "json" CLIENT_ID = ENV.fetch("CLIENT_ID") CLIENT_SECRET = ENV.fetch("CLIENT_SECRET") def parse_response(response) case response when Net::HTTPOK JSON.parse(response.body) else puts response puts response.body {} end end def exchange_code(code) params = { "client_id" => CLIENT_ID, "client_secret" => CLIENT_SECRET, "code" => code } result = Net::HTTP.post( URI("https://github.com/login/oauth/access_token"), URI.encode_www_form(params), {"Accept" => "application/json"} ) parse_response(result) end get "/" do link = '<a href="https://github.com/login/oauth/authorize?client_id=<%= CLIENT_ID %>">Login with GitHub</a>' erb link end get "CALLBACK_URL" do code = params["code"] token_data = exchange_code(code) if token_data.key?("access_token") token = token_data["access_token"] render = "Successfully authorized! Got code #{code} and exchanged it for a user access token ending in #{token[-9..-1]}." erb render else render = "Authorized, but unable to exchange code #{code} for token." erb render end end
- Функция
-
При необходимости проверьте ход выполнения:
- В терминале в каталоге, где
app.rb
хранится, выполните командуruby app.rb
. Локальный сервер Sinatra должен запускаться. - В браузере перейдите по адресу
http://localhost:4567
. Вы увидите ссылку с текстом "Вход с помощью GitHub". - Щелкните ссылку "Вход с помощью GitHub".
- Если вам будет предложено сделать это, авторизуйте приложение.
- Страница URL-адреса обратного вызова, достигнутная путем нажатия ссылки "Вход с помощью GitHub", а затем авторизация приложения при появлении запроса на это, должна отображать текст, аналогичный "Успешно авторизовано! Получил код 4acd44861aeda86dacce и обменил его на маркер доступа пользователя, заканчивающийся 2zU5kQziE".
- В терминале, где работает Sinatra, остановите сервер, введя ctrl+C.
- В терминале в каталоге, где
-
Теперь, когда у вас есть маркер доступа пользователя, можно использовать маркер для выполнения запросов API от имени пользователя. Например:
Добавьте эту функцию, чтобы
app.rb
получить сведения о пользователе с конечной/user
точкой REST API:Ruby def user_info(token) uri = URI("https://api.github.com/user") result = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| body = {"access_token" => token}.to_json auth = "Bearer #{token}" headers = {"Accept" => "application/json", "Content-Type" => "application/json", "Authorization" => auth} http.send_request("GET", uri.path, body, headers) end parse_response(result) end
def user_info(token) uri = URI("https://api.github.com/user") result = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| body = {"access_token" => token}.to_json auth = "Bearer #{token}" headers = {"Accept" => "application/json", "Content-Type" => "application/json", "Authorization" => auth} http.send_request("GET", uri.path, body, headers) end parse_response(result) end
Обновите обработчик обратного вызова, чтобы вызвать
user_info
функцию и отобразить имя пользователя и имя входа GitHub. Не забудьте заменитьCALLBACK_URL
URL-адрес обратного вызова для приложения, минус домен.Ruby get "CALLBACK_URL" do code = params["code"] token_data = exchange_code(code) if token_data.key?("access_token") token = token_data["access_token"] user_info = user_info(token) handle = user_info["login"] name = user_info["name"] render = "Successfully authorized! Welcome, #{name} (#{handle})." erb render else render = "Authorized, but unable to exchange code #{code} for token." erb render end end
get "CALLBACK_URL" do code = params["code"] token_data = exchange_code(code) if token_data.key?("access_token") token = token_data["access_token"] user_info = user_info(token) handle = user_info["login"] name = user_info["name"] render = "Successfully authorized! Welcome, #{name} (#{handle})." erb render else render = "Authorized, but unable to exchange code #{code} for token." erb render end end
-
Проверьте код в полном примере кода в следующем разделе. Вы можете протестировать код, выполнив действия, описанные в разделе "Тестирование" ниже полного примера кода.
Полный пример кода
Это полный пример кода, описанный в предыдущем разделе.
Замените CALLBACK_URL
URL-адрес обратного вызова для приложения, минус домен. Например, если URL-адрес обратного вызова имеет значение http://localhost:4567/github/callback
, замените CALLBACK_URL
на /github/callback
.
require "sinatra" require "dotenv/load" require "net/http" require "json" CLIENT_ID = ENV.fetch("CLIENT_ID") CLIENT_SECRET = ENV.fetch("CLIENT_SECRET") def parse_response(response) case response when Net::HTTPOK JSON.parse(response.body) else puts response puts response.body {} end end def exchange_code(code) params = { "client_id" => CLIENT_ID, "client_secret" => CLIENT_SECRET, "code" => code } result = Net::HTTP.post( URI("https://github.com/login/oauth/access_token"), URI.encode_www_form(params), {"Accept" => "application/json"} ) parse_response(result) end def user_info(token) uri = URI("https://api.github.com/user") result = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| body = {"access_token" => token}.to_json auth = "Bearer #{token}" headers = {"Accept" => "application/json", "Content-Type" => "application/json", "Authorization" => auth} http.send_request("GET", uri.path, body, headers) end parse_response(result) end get "/" do link = '<a href="https://github.com/login/oauth/authorize?client_id=<%= CLIENT_ID %>">Login with GitHub</a>' erb link end get "CALLBACK_URL" do code = params["code"] token_data = exchange_code(code) if token_data.key?("access_token") token = token_data["access_token"] user_info = user_info(token) handle = user_info["login"] name = user_info["name"] render = "Successfully authorized! Welcome, #{name} (#{handle})." erb render else render = "Authorized, but unable to exchange code #{code} for token." erb render end end
require "sinatra"
require "dotenv/load"
require "net/http"
require "json"
CLIENT_ID = ENV.fetch("CLIENT_ID")
CLIENT_SECRET = ENV.fetch("CLIENT_SECRET")
def parse_response(response)
case response
when Net::HTTPOK
JSON.parse(response.body)
else
puts response
puts response.body
{}
end
end
def exchange_code(code)
params = {
"client_id" => CLIENT_ID,
"client_secret" => CLIENT_SECRET,
"code" => code
}
result = Net::HTTP.post(
URI("https://github.com/login/oauth/access_token"),
URI.encode_www_form(params),
{"Accept" => "application/json"}
)
parse_response(result)
end
def user_info(token)
uri = URI("https://api.github.com/user")
result = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
body = {"access_token" => token}.to_json
auth = "Bearer #{token}"
headers = {"Accept" => "application/json", "Content-Type" => "application/json", "Authorization" => auth}
http.send_request("GET", uri.path, body, headers)
end
parse_response(result)
end
get "/" do
link = '<a href="https://github.com/login/oauth/authorize?client_id=<%= CLIENT_ID %>">Login with GitHub</a>'
erb link
end
get "CALLBACK_URL" do
code = params["code"]
token_data = exchange_code(code)
if token_data.key?("access_token")
token = token_data["access_token"]
user_info = user_info(token)
handle = user_info["login"]
name = user_info["name"]
render = "Successfully authorized! Welcome, #{name} (#{handle})."
erb render
else
render = "Authorized, but unable to exchange code #{code} for token."
erb render
end
end
Тестирование
В этом руководстве предполагается, что код приложения хранится в файле с именем app.rb
и используется URL-адрес по умолчанию для локального приложения Sinatra. http://localhost:4567
-
В терминале в каталоге, где
app.rb
хранится, выполните командуruby app.rb
. Локальный сервер Sinatra должен запускаться. -
В браузере перейдите по адресу
http://localhost:4567
. Вы увидите ссылку с текстом "Вход с помощью GitHub". -
Щелкните ссылку "Вход с помощью GitHub".
Если вы не авторизовано приложение, щелкните ссылку,
https://github.com/login/oauth/authorize?client_id=CLIENT_ID
в которойCLIENT_ID
находится идентификатор клиента вашего приложения. Это страница GitHub, которая предлагает пользователям авторизовать приложение. Если нажать кнопку, чтобы авторизовать приложение, вы перейдете по URL-адресу обратного вызова для приложения.Если вы ранее авторизовано приложение и авторизация не были отменены, вы пропустите запрос авторизации и перейдите непосредственно к URL-адресу обратного вызова. Вы можете отозвать предыдущую авторизацию, если вы хотите просмотреть запрос на авторизацию. Дополнительные сведения см. в разделе Проверка и отзыв авторизации приложений GitHub.
-
Страница URL-адреса обратного вызова, достигнутная путем нажатия ссылки "Вход с помощью GitHub", а затем авторизация приложения при появлении запроса на это, должна отображать текст, аналогичный "Успешно авторизовано! Добро пожаловать, Мона Лиза (октокат)."
-
В терминале, где работает Sinatra, остановите сервер, введя ctrl+C.
Следующие шаги
Безопасное хранение секрета клиента
Никогда не следует публиковать секрет клиента приложения. В этом руководстве хранится секрет клиента в файле с gitignored .env
и он обращается к значению.ENV.fetch
При развертывании приложения следует выбрать безопасный способ хранения секрета клиента и обновить код, чтобы получить соответствующее значение.
Например, секрет можно сохранить в переменной среды на сервере, на котором развернуто приложение. Вы также можете использовать службу управления секретами, например Azure Key Vault.
Обновление URL-адреса обратного вызова для развертывания
В этом руководстве используется URL-адрес обратного вызова, начиная с http://localhost:4567
. Однако при http://localhost:4567
запуске сервера Sinatra доступен только локально на компьютере. Перед развертыванием приложения необходимо обновить URL-адрес обратного вызова, чтобы использовать URL-адрес обратного вызова, используемый в рабочей среде. Дополнительные сведения об обновлении URL-адреса обратного вызова для приложения см. в разделе "[AUTOTITLE" и "Изменение регистрации приложения GitHub](/apps/creating-github-apps/setting-up-a-github-app/about-the-user-authorization-callback-url)".
Обработка нескольких URL-адресов обратного вызова
В этом руководстве используется один URL-адрес обратного вызова, но приложение может иметь до 10 URL-адресов обратного вызова. Если вы хотите использовать несколько URL-адресов обратного вызова:
- Добавьте дополнительные URL-адреса обратного вызова в приложение. Дополнительные сведения о добавлении URL-адресов обратного вызова см. в разделе "Изменение регистрации приложения GitHub".
- При ссылке
https://github.com/login/oauth/authorize``redirect_uri
на параметр запроса используйте параметр запроса для перенаправления пользователей на нужный URL-адрес обратного вызова. Дополнительные сведения см. в разделе Создание маркера доступа пользователя для приложения GitHub. - В коде приложения обработайте каждый URL-адрес обратного вызова, аналогичный блоку кода, начиная с
get "CALLBACK_URL" do
.
Указание дополнительных параметров
При ссылке https://github.com/login/oauth/authorize
на нее можно передать дополнительные параметры запроса. Дополнительные сведения см. в разделе Создание маркера доступа пользователя для приложения GitHub.
В отличие от традиционного маркера OAuth, маркер доступа пользователя не использует области, поэтому нельзя указывать области с помощью scope
параметра. Вместо этого он использует точные разрешения. Маркер доступа пользователя имеет только разрешения, имеющиеся как у пользователя, так и у приложения.
Настройка кода в соответствии с потребностями приложения
В этом руководстве показано, как отобразить сведения о пользователе, прошедшем проверку подлинности, но вы можете настроить этот код для выполнения других действий. Не забудьте обновить разрешения приложения, если приложению требуются дополнительные разрешения для запросов API, которые вы хотите сделать. Дополнительные сведения см. в разделе Выбор разрешений для приложения GitHub.
В этом руководстве хранится весь код в одном файле, но может потребоваться переместить функции и компоненты в отдельные файлы.
Безопасное хранение маркеров
В этом руководстве создается маркер доступа пользователей. Если вы не отказались от истечения срока действия маркеров доступа пользователей, срок действия маркера доступа пользователя истекает через восемь часов. Вы также получите маркер обновления, который может повторно создать маркер доступа пользователя. Дополнительные сведения см. в разделе Обновление маркеров доступа пользователей.
Если вы планируете дальше взаимодействовать с API-интерфейсами GitHub, следует сохранить маркер для дальнейшего использования. Если вы решили сохранить маркер доступа пользователя или маркер обновления, необходимо безопасно хранить его. Никогда не следует публиковать маркер.
Дополнительные сведения см. в разделе Рекомендации по созданию приложения GitHub.
Применение рекомендаций
Вам следует следовать рекомендациям по использованию данных GitHub App. Дополнительные сведения см. в разделе Рекомендации по созданию приложения GitHub.