ちゃなべの備忘録

ほぼ備忘録です。

GithubのOIDCのチュートリアルをやってみた【備忘録】

はじめに

GithubのOIDCを実装したかったんだけど、ちょっとチュートリアルを見つけたのでそれをパットやってみる。

今回やるのはこれ

docs.github.com

実装

GitHubAppの登録

まずはGithubで準備することがある。

docs.github.com

  1. GithubAppsの設定ページにアクセス
  2. [New GitHub App] をクリック
  3. 認証をする
  4. 必要な情報を入力する
    • GitHub App name →oidc-tutorial
    • 説明文 → 適当に
    • Homepage URL → GithubのレポジトリURL
    • Active → 解除
  5. 作成完了

Rubyの開発環境整え

参考はこちら

qiita.com

まずは以下のファイルを準備

  1. .gitignore
  2. Gemfile
  3. Gemfile.lock
  4. docker-compose.yaml
  5. Dockerfile
  6. app.rb
コマンドで1.2.3.のファイルを準備
# gitignoreをダウンロード
$ curl https://raw.githubusercontent.com/github/gitignore/master/Ruby.gitignore -o .gitignore

# GemfileとGemfile.lockを生成とダウンロード
$ docker run --rm --volume $(pwd):/app --workdir /app ruby:2.7-slim bundle init
$ docker run --rm --volume $(pwd):/app --workdir /app ruby:2.7-slim bundle add sinatra 
$ docker run --rm --volume $(pwd):/app --workdir /app ruby:2.7-slim bundle add dotenv
手動でDockerfileとdocker-compose.yamlを作成

ruby/appディレクトリを使うのが一般的なのか?まぁとりあえず従いますよ。

FROM ruby:2.7-slim
WORKDIR /app

COPY Gemfile ./
COPY Gemfile.lock ./
RUN bundle config --local set path 'vendor/bundle'
RUN bundle install

CMD bundle exec ruby app.rb
version: '3'
services:
  app:
    build: .
    volumes:
      - .:/app
      - /app/vendor/bundle
    ports:
      - 4567:4567
app.rb (アプリ自体のファイル) を作成

まぁ作成

require 'sinatra'

configure do
  set :bind, '0.0.0.0'
end

get '/' do
  'Hello Sinatra!'
end
起動

まぁ、ついたね。

OIDCの設定を進める

  1. GitHub Appsの設定ページを開く
  2. [Edit] を押す
  3. [Generate a new client secret] で新しいクライアントシークレットを発行する。
  4. クライアントIDと発行されたクライアントシークレットを.envに保存する
  5. gitignoreに.envを追加
CLIENT_ID="YOUR_CLIENT_ID"
CLIENT_SECRET="YOUR_CLIENT_SECRET"
- # .env
+ .env

ログインボタンを設置

app.rbに以下の記述をして、Loginボタンを設置します。

require "sinatra"
require "dotenv/load"
require "net/http"
require "json"

CLIENT_ID = ENV.fetch("CLIENT_ID")
CLIENT_SECRET = ENV.fetch("CLIENT_SECRET")

configure do
  set :bind, '0.0.0.0'
end

get "/" do
  link = '<a href="https://github.com/login/oauth/authorize?client_id=<%= CLIENT_ID %>">Login with GitHub</a>'
  erb link
end

linkをクリックすると、Callback先を指定してないので、こうなる。

Callbackを設定

sinatraでCallbackURLを設定

以下を追記

+  get "/github/callback" do
+    code = params["code"]
+    render = "Successfully authorized! Got code #{code}."
+    erb render
+  end
Githubに設定

GitHubAppの設定編集に行き、[Callback URL]を編集。

僕の場合は、http://localhost:4567/github/callback と入力

保存も忘れずに

やってみる

いいね、ちゃんとcodeも受け取れる。

access_tokenを受け取る

先程もらった、codeをもとに、access_tokenをもらいにいこう。

以下を追記した。

追記した内容は、

  • parse_response 関数で、GitHub API からの応答を解析します。
  • exchange_code 関数で、code パラメーターをユーザー アクセス トークンと交換します。
  • コールバック URL 要求のハンドラーで exchange_code を呼び出して、code パラメーターをユーザー アクセス トークンと交換するようになります。
  • コールバック ページに、トークンが生成されたことを示すテキストが表示されるようになります。 トークン生成が成功しなかった場合は、ページにそのエラーが示されます。
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 "/github/callback" 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

じゃあこれでやってみると、ちゃんと受け取れる!

access_tokenを使って、REST APIを叩く。

じゃあついにaccess_tokenを得ることができたので、REST APIを叩いてみよう。

Githubの設定変更

いつものようにGithubAppsを変更する。

  1. GithubAppsの設定ページにいき、[Edit]を押す
  2. 左のサイドバーの[Permissions & events]をクリック
  3. Repository permissionsの AdministrationContents をRead-onlyに変更する
  4. 保存
app.rbの修正

以下を追記して、修正

def get_repos(token)
  uri = URI("https://api.github.com/user/repos")

  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 "/github/callback" do
  code = params["code"]

  token_data = exchange_code(code)

  if token_data.key?("access_token")
    token = token_data["access_token"]

    repos = get_repos(token)

    render = "Successfully authorized! Welcome, #{repos})."
    erb render
  else
    render = "Authorized, but unable to exchange code #{code} for token."
    erb render
  end
end

すると、callbackページで、レポジトリの一覧を取得することができるようになった。

だけど、これ、privateレポジトリの取得ができないっぽい? どうやら、このappをinstallしたレポジトリしかprivateに関しては取得できないらしい。

えーーーっと、やりたいことと違ったかな。?

次やりたいこと

  • 静的Access Tokenを発行して、それだとprivateレポジトリの中身も見れるのか確認。
  • GitHub OAuthはどのように使えるのか調査