소개
이 자습서에서는 웹 사이트에 대한 "GitHub로 로그인" 단추를 빌드하는 방법을 보여 줍니다. 웹 사이트는 GitHub App를 사용하여 웹 애플리케이션 흐름을 통해 사용자 액세스 토큰을 생성합니다. 그런 다음, 웹 사이트는 사용자 액세스 토큰을 사용하여 인증된 사용자를 대신하여 API 요청을 수행합니다.
이 자습서에서는 Ruby를 사용하지만 웹 개발에 사용되는 프로그래밍 언어와 함께 웹 애플리케이션 흐름을 사용할 수 있습니다.
웹 애플리케이션 흐름 및 사용자 액세스 토큰 정보
앱의 작업을 사용자에게 특성화하려면 앱에서 사용자 액세스 토큰을 사용해야 합니다. 자세한 내용은 "사용자를 대신하여 GitHub 앱 인증"을 참조하세요.
GitHub App에 대한 사용자 액세스 토큰을 생성하는 방법에는 웹 애플리케이션 흐름 및 디바이스 흐름의 두 가지가 있습니다. 앱이 웹 인터페이스에 액세스할 수 있는 경우 웹 애플리케이션 흐름을 사용해야 합니다. 앱에 웹 인터페이스에 대한 액세스 권한이 없는 경우 대신 디바이스 흐름을 사용해야 합니다. 자세한 내용은 "GitHub 앱 대한 사용자 액세스 토큰 생성" 및 "GitHub 앱 사용하여 CLI 빌드.
필수 구성 요소
이 자습서에서는 GitHub App을(를) 이미 만들었다고 가정합니다. 앱 만들기에 대한 자세한 내용은 "GitHub 앱 만들기"을 참조하세요.
이 자습서를 따르기 전에 앱에 대한 콜백 URL을 설정해야 합니다. 이 자습서에서는 기본 URL http://localhost:4567
이 인 로컬 Sinatra 서버를 사용합니다. 예를 들어 로컬 Sinatra 애플리케이션의 기본 URL을 사용하려면 콜백 URL이 일 http://localhost:4567/github/callback
수 있습니다. 앱을 배포할 준비가 되면 콜백 URL을 변경하여 라이브 서버 주소를 사용할 수 있습니다. 앱의 콜백 URL을 업데이트하는 방법에 대한 자세한 내용은 "GitHub 앱 수정" 및 "사용자 권한 부여 콜백 URL 정보"을 참조하세요.
이 자습서에서는 Ruby 및 Ruby 템플릿 시스템인 ERB에 대한 기본적인 이해가 있다고 가정합니다. 자세한 내용은 Ruby 및 ERB를 참조하세요.
종속성 설치
이 자습서에서는 Ruby gem Sinatra를 사용하여 Ruby를 사용하여 웹 애플리케이션을 만듭니다. 자세한 내용은 Sinatra 추가 정보를 참조하세요.
이 자습서에서는 Ruby gem dotenv를 사용하여 파일에 저장된 값에 .env
액세스합니다. 자세한 내용은 dotenv 추가 정보를 참조하세요.
이 자습서를 수행하려면 Ruby 프로젝트에 Sinatra 및 dotenv gems를 설치해야 합니다. 예를 들어 Bundler를 사용하여 이 작업을 수행할 수 있습니다.
-
Bundler가 아직 설치되지 않은 경우 터미널에서 다음 명령을 실행합니다.
gem install bundler
-
앱에 대한 Gemfile이 아직 없는 경우 터미널에서 다음 명령을 실행합니다.
bundle init
-
앱에 대한 Gemfile.lock이 아직 없는 경우 터미널에서 다음 명령을 실행합니다.
bundle install
-
터미널에서 다음 명령을 실행하여 gem을 설치합니다.
bundle add sinatra
bundle add dotenv
클라이언트 ID 및 클라이언트 암호 저장
이 자습서에서는 환경 변수에 클라이언트 ID 및 클라이언트 비밀을 저장하고 를 사용하여 액세스하는 ENV.fetch
방법을 보여 줍니다. 앱을 배포할 때 클라이언트 ID 및 클라이언트 암호를 저장하는 방법을 변경하려고 합니다. 자세한 내용은 "클라이언트 비밀 안전하게 저장"을 참조하세요.
-
계정 설정으로 이동합니다.
- 개인 계정이 소유하는 GitHub App의 경우 페이지 오른쪽 위 모서리에서 프로필 사진을 클릭한 다음, 설정을 클릭합니다.
- 조직이 소유하는 GitHub App의 경우 페이지 오른쪽 위 모서리에서 프로필 사진을 클릭한 다음, 조직을 클릭합니다. 그런 다음, 조직 오른쪽에서 설정을 클릭합니다.
-
왼쪽 사이드바에서 개발자 설정을 클릭합니다.
-
왼쪽 사이드바에서 GitHub Apps를 클릭합니다.
-
작업하려는 GitHub App 옆에 있는 편집을 클릭합니다.
-
앱의 설정 페이지에서 앱의 클라이언트 ID를 찾습니다. 다음 단계에서 파일에 추가
.env
합니다. 클라이언트 ID는 앱 ID와 다릅니다. -
앱의 설정 페이지에서 새 클라이언트 암호 생성을 클릭합니다. 다음 단계에서 파일에 클라이언트 암호를
.env
추가합니다. -
와 동일한 수준에서
Gemfile
라는.env
파일을 만듭니다. -
프로젝트에 파일이 아직
.gitignore
없는 경우 와 동일한 수준에서Gemfile
파일을 만듭니.gitignore
다. -
파일에 를 추가
.env
합니다.gitignore
. 이렇게 하면 실수로 클라이언트 암호를 커밋할 수 없습니다. 파일에 대한.gitignore
자세한 내용은 "Ignoring files(파일 무시)"을 참조하세요. -
파일에 다음 내용을 추가합니다
.env
. 을 앱의 클라이언트 ID로 바꿉니다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"
sinatra
및dotenv/load
종속성은 이전에 설치한 gem을 사용합니다.net/http
및json
는 Ruby 표준 라이브러리의 일부입니다. -
다음 코드를 에
app.rb
추가하여 파일에서.env
앱의 클라이언트 ID 및 클라이언트 암호를 가져옵니다.Ruby CLIENT_ID = ENV.fetch("CLIENT_ID") CLIENT_SECRET = ENV.fetch("CLIENT_SECRET")
-
다음 코드를 에
app.rb
추가하여 사용자에게 앱을 인증하라는 메시지를 표시하는 링크를 표시합니다.Ruby get "/" do link = '<a href="http(s)://HOSTNAME/login/oauth/authorize?client_id=<%= CLIENT_ID %>">Login with GitHub</a>' erb link end
-
에 다음 코드를
app.rb
추가하여 앱의 콜백 URL에 대한 요청을 처리하고 요청에서 매개 변수를code
가져옵니다. 을 도메인을 뺀 앱의 콜백 URL로 바꿉CALLBACK_URL
니다. 예를 들어 콜백 URL이 인 경우 를 로/github/callback
대체CALLBACK_URL
합니다http://localhost:4567/github/callback
.Ruby 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="http(s)://HOSTNAME/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로 로그인" 링크를 클릭합니다.
앱에 권한을 부여하지 않은 경우 링크를 클릭하면 로 이동합니다
http(s)://HOSTNAME/login/oauth/authorize?client_id=CLIENT_ID
. 여기서CLIENT_ID
는 앱의 클라이언트 ID입니다. 사용자에게 앱에 권한을 부여하라는 메시지를 표시하는 GitHub 페이지입니다. 단추를 클릭하여 앱에 권한을 부여하면 앱의 콜백 URL로 이동합니다.이전에 앱에 권한을 부여했고 권한 부여가 해지되지 않은 경우 권한 부여 프롬프트를 건너뛰고 콜백 URL로 직접 이동합니다. 권한 부여 프롬프트를 보려면 이전 권한 부여를 해지할 수 있습니다. 자세한 내용은 "GitHub 앱의 권한 부여 검토 및 취소"을 참조하세요.
-
"GitHub로 로그인" 링크를 클릭한 다음, 이렇게 하라는 메시지가 표시되면 앱에 권한을 부여하여 도달한 콜백 URL 페이지에는 "성공적으로 승인되었습니다! 코드 agc622abb6135be5d1f2가 있습니다."
-
Sinatra가 실행 중인 터미널에서 Ctrl+C를 입력하여 서버를 중지합니다.
-
-
의
app.rb
콘텐츠를 다음 코드로 바꿉니다. 여기서CALLBACK_URL
는 앱의 콜백 URL에서 도메인을 뺀 값입니다.이 코드는 사용자 액세스 토큰에 대한 매개 변수를
code
교환하는 논리를 추가합니다.- 함수는
parse_response
GitHub API의 응답을 구문 분석합니다. - 함수는
exchange_code
사용자 액세스 토큰에code
대한 매개 변수를 교환합니다. - 콜백 URL 요청에 대한 처리기는 이제 를 호출
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("http(s)://HOSTNAME/login/oauth/access_token"), URI.encode_www_form(params), {"Accept" => "application/json"} ) parse_response(result) end get "/" do link = '<a href="http(s)://HOSTNAME/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로 로그인" 링크를 클릭합니다.
- 이렇게 하라는 메시지가 표시되면 앱에 권한을 부여합니다.
- "GitHub로 로그인" 링크를 클릭한 다음 앱에 권한을 부여하여 도달한 콜백 URL 페이지에는 "성공적으로 승인되었습니다! 코드 4acd44861aeda86dacce를 가지고 2zU5kQziE로 끝나는 사용자 액세스 토큰으로 교환했습니다."
- Sinatra가 실행 중인 터미널에서 Ctrl+C를 입력하여 서버를 중지합니다.
- 터미널의 가 저장된 디렉터리
-
이제 사용자 액세스 토큰이 있으므로 토큰을 사용하여 사용자를 대신하여 API 요청을 만들 수 있습니다. 예:
REST API 엔드포인트를
app.rb
사용하여 사용자에 대한 정보를 얻으려면 에 이 함수를/user
추가합니다.Ruby def user_info(token) uri = URI("http(s)://HOSTNAME/api/v3/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 로그인을 표시합니다. 를 앱의 콜백 URL(도메인 제외)으로 바꿔CALLBACK_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
-
다음 섹션의 전체 코드 예제에 대해 코드를 확인합니다. 전체 코드 예제 아래의 "테스트" 섹션에 설명된 단계에 따라 코드를 테스트할 수 있습니다.
전체 코드 예제
이전 섹션에서 설명한 전체 코드 예제입니다.
를 앱의 콜백 URL(도메인 제외)으로 바꿉 CALLBACK_URL
니다. 예를 들어 콜백 URL이 인 경우 를 로 /github/callback
대체 CALLBACK_URL
합니다http://localhost:4567/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("http(s)://HOSTNAME/login/oauth/access_token"),
URI.encode_www_form(params),
{"Accept" => "application/json"}
)
parse_response(result)
end
def user_info(token)
uri = URI("http(s)://HOSTNAME/api/v3/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="http(s)://HOSTNAME/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
파일에 저장되고 로컬 Sinatra 애플리케이션 http://localhost:4567
에 대한 기본 URL을 사용하고 있다고 가정합니다.
-
터미널의 가 저장된 디렉터리
app.rb
에서 를 실행합니다ruby app.rb
. 로컬 Sinatra 서버가 시작되어야 합니다. -
브라우저에서
http://localhost:4567
로 이동합니다. "GitHub로 로그인"이라는 텍스트가 있는 링크가 표시됩니다. -
"GitHub로 로그인" 링크를 클릭합니다.
앱에 권한을 부여하지 않은 경우 링크를 클릭하면 앱의 클라이언트 ID인 로
CLIENT_ID
이동합니다http(s)://HOSTNAME/login/oauth/authorize?client_id=CLIENT_ID
. 이 페이지는 사용자에게 앱에 권한을 부여하라는 메시지를 표시하는 GitHub 페이지입니다. 단추를 클릭하여 앱에 권한을 부여하면 앱의 콜백 URL로 이동합니다.이전에 앱에 권한을 부여했고 권한 부여가 해지되지 않은 경우 권한 부여 프롬프트를 건너뛰고 대신 콜백 URL로 직접 이동합니다. 권한 부여 프롬프트를 보려면 이전 권한 부여를 해지할 수 있습니다. 자세한 내용은 "GitHub 앱의 권한 부여 검토 및 취소"을 참조하세요.
-
"GitHub로 로그인" 링크를 클릭한 다음 앱에 권한을 부여하여 도달한 콜백 URL 페이지에는 "성공적으로 승인되었습니다! 오신 것을 환영합니다, 모나리자 (옥토캣)."
-
Sinatra가 실행 중인 터미널에서 Ctrl+C를 입력하여 서버를 중지합니다.
다음 단계
클라이언트 암호를 안전하게 저장
앱의 클라이언트 암호를 공개해서는 안 됩니다. 이 자습서에서는 클라이언트 암호를 gitignored .env
파일에 저장하고 를 사용하여 값 ENV.fetch
에 액세스했습니다. 앱을 배포할 때 클라이언트 암호를 저장하고 코드를 업데이트하여 값을 적절하게 가져오는 안전한 방법을 선택해야 합니다.
예를 들어 애플리케이션이 배포된 서버의 환경 변수에 비밀을 저장할 수 있습니다. Azure Key Vault 같은 비밀 관리 서비스를 사용할 수도 있습니다.
배포에 대한 콜백 URL 업데이트
이 자습서에서는 로 시작하는 콜백 URL을 사용했습니다 http://localhost:4567
. 그러나 http://localhost:4567
는 Sinatra 서버를 시작할 때만 컴퓨터에서 로컬로 사용할 수 있습니다. 앱을 배포하기 전에 프로덕션에서 사용하는 콜백 URL을 사용하도록 콜백 URL을 업데이트해야 합니다. 앱의 콜백 URL을 업데이트하는 방법에 대한 자세한 내용은 "GitHub 앱 수정" 및 "사용자 권한 부여 콜백 URL 정보"을 참조하세요.
여러 콜백 URL 처리
이 자습서에서는 단일 콜백 URL을 사용했지만 앱에는 최대 10개 콜백 URL이 있을 수 있습니다. 여러 콜백 URL을 사용하려는 경우:
- 앱에 추가 콜백 URL을 추가합니다. 콜백 URL을 추가하는 방법에 대한 자세한 내용은 "GitHub 앱 수정.
- 에
http(s)://HOSTNAME/login/oauth/authorize
연결할 때 쿼리 매개 변수를redirect_uri
사용하여 사용자를 원하는 콜백 URL로 리디렉션합니다. 자세한 내용은 "GitHub 앱 대한 사용자 액세스 토큰 생성"을 참조하세요. - 앱 코드에서 에서 시작하는 코드 블록과 유사한 각 콜백 URL을 처리합니다
get "CALLBACK_URL" do
.
추가 매개 변수 지정
에 http(s)://HOSTNAME/login/oauth/authorize
연결하면 추가 쿼리 매개 변수를 전달할 수 있습니다. 자세한 내용은 "GitHub 앱 대한 사용자 액세스 토큰 생성"을 참조하세요.
기존 OAuth 토큰과 달리 사용자 액세스 토큰은 범위를 사용하지 않으므로 매개 변수를 통해 scope
범위를 지정할 수 없습니다. 대신 세분화된 권한을 사용합니다. 사용자 액세스 토큰에는 사용자와 앱 모두에 있는 권한만 있습니다.
앱의 요구 사항에 맞게 코드 조정
이 자습서에서는 인증된 사용자에 대한 정보를 표시하는 방법을 보여 주었지만 이 코드를 조정하여 다른 작업을 수행할 수 있습니다. 앱에 만들려는 API 요청에 대한 추가 권한이 필요한 경우 앱의 권한을 업데이트해야 합니다. 자세한 내용은 "GitHub 앱 대한 권한 선택"을 참조하세요.
이 자습서에서는 모든 코드를 단일 파일에 저장했지만 함수와 구성 요소를 별도의 파일로 이동할 수 있습니다.
토큰을 안전하게 저장
이 자습서에서는 사용자 액세스 토큰을 생성합니다. 사용자 액세스 토큰에 대한 만료를 옵트아웃하지 않는 한 사용자 액세스 토큰은 8시간 후에 만료됩니다. 또한 사용자 액세스 토큰을 다시 생성할 수 있는 새로 고침 토큰을 받게 됩니다. 자세한 내용은 "사용자 액세스 토큰 새로 고침"을 참조하세요.
GitHub의 API와 추가로 상호 작용하려는 경우 나중에 사용할 수 있도록 토큰을 저장해야 합니다. 사용자 액세스 토큰을 저장하거나 토큰을 새로 고치도록 선택하는 경우 안전하게 저장해야 합니다. 토큰을 공개해서는 안 됩니다.