Help us understand the problem. What is going on with this article?

[iOS]OAuth認証を極める(GitHubAPIで学ぶOAuth認証のフロー)

More than 1 year has passed since last update.

アプリでGitHubAPIを使用する

<対象>
・OAuth認証のフローを知りたい
・GitHubAPIをつかいたい

GitHubでは、検索やリポジトリの読み書き、プルリクエストの生成・更新・削除などいろいろなAPIが提供されています。
GitHubAPI

これらは
アクセストークンなしでAPIを使用することもできるのですが、その場合はAPIでできることが一部制限されてしまいます。例えば、SearchAPIには以下のような注釈があります。

Note: You must authenticate to search for code across all public repositories.
パブリックなリポジトリを対象に検索を実行するには認証が必要です

というわけで、APIの全機能を使うためにはGitHub認証が必要になるわけです。

本記事ではiOSアプリでGitHub認証しアクセストークンを取得するまで(OAuth認証の流れとアプリ側の実装)をまとめました!

OAuth認証のフロー

・事前準備
ここからGitHubのアプリを新規に作成します。
必要事項を記入し、完了するとClient IDClient Secretが発行されます。

でここからがアプリ内での処理です。

① 認可リクエスト
GithubのAPIを使ったアプリのサービスを受けたいので、「認可リクエスト」を行う
→ WebViewなどで認証画面を表示する
→ ユーザーはGithubにログインし、アプリに代理アクセスすることを許可する(連携認証の許可)
→ GitHubからコールバック
→ GitHubから認証コードをレスポンスとしてもらう

② アクセストークン取得
もらったコードを使ってアプリはGithubの「アクセストークン交換場所 (Token Endpoint)」にアクセスします。
その際、認証コードを「アクセストークン」に変換する為に、アプリは事前の登録で取得したクレデンシャル(Client IDClient Secret)で認証を受けます。 (クライアント認証)
正しく認証されると、コードと引き換えにアクセストークンをもらいます。

以上の流れは「コードフロー」と呼ばれ、基本的なOAuthのフローです。

図にすると以下のような感じです。
スクリーンショット 2018-05-05 4.31.04.png

アプリで実装してみる

流れがわかったので、実装に落とし込んでみます。

(以下ではライブラリとして、Alamofire, Swiftyjsonを利用しています)

① 認可リクエスト

let gitOuthUrl = URL(string: "https://github.com/login/oauth/authorize?client_id={ 
取得したクライアントID }&scope=public_repo")
let gitOuthRequest = URLRequest(url: gitOuthUrl!)
webView.load(gitOuthRequest)

上記ではwebViewで認可リクエストを送っています。
scope=public_repoはパブリックなリポジトリに対する読み書き権限を要求していることを示します。なくてもOKです。参考
で、以下のように連携認証のためにログインが求められます。

Simulator Screen Shot - iPhone X - 2018-05-06 at 00.04.24.png

ここでログインを実行するとコールバックURLが実行され、認証コードを取得できるので、それを取得する処理を書きます。

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { 

        // 設定しているコールバックURLのスキーム、ホスト名で分岐処理       
        if navigationAction.request.url?.scheme == "yyokii", navigationAction.request.url?.host == "GithubSearchApp"{
            dismiss(animated: true, completion: nil)

            // 認証コード取得
            let code = getQueryStringParameter(url: (navigationAction.request.url?.absoluteString)!, param: "code")


            // ② アクセストークン取得処理
            let githubAPIManager = GithubAPIManager.sharedManager
            githubAPIManager.getAccessToken(code: code, completion: nil)

            decisionHandler(WKNavigationActionPolicy.allow);
}


static func getQueryStringParameter(url: String, param: String) -> String? {
        guard let url = URLComponents(string: url) else { return nil }
        return url.queryItems?.first(where: { $0.name == param })?.value
    }

ここで使用されるコールバックURLはGithHubでアプリ登録したときに設定したURLとなります。
コールバックURLが叩かれたときにクエリパラメーターに付与されている認証コードを取得しています。

② アクセストークン取得処理

    /// アクセストークンを取得
    ///
    /// - Parameter code: このコードとアクセストークンを交換
    func getAccessToken(code: String!, completion: @escaping () -> Void) {
        self.code = code
        let url = "https://github.com/login/oauth/access_token"

        // 受信可能なレスポンスデータのメディアタイプを指定
        let headers = [
            "ACCEPT": "application/json"
        ]

        // 認証に必要な値を設定
        let parameters: Parameters = [
            "client_id": self.clientId,
            "client_secret": self.clientSecret,
            "code": self.code
            ]

        Alamofire.request(url, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseJSON{ response in

            switch response.result {
            case .success:
                let json = JSON(response.result.value!)

                // アクセストークンを取得
                let accessToken = json["access_token"].string
                UserDefaults.standard.set(accessToken!, forKey: ACCESS_TOKEN)
                completion()
            case .failure(let error):
                print(error)
            }
        }
    }

ヘッダー、リクエストパラメーターを設定し、"https://github.com/login/oauth/access_token"をPOST通信で叩きます。でそのレスポンスからアクセストークンを取得しています。

このアクセストークンを使用してAPIを叩くには、ヘッダーに
"Authorization": "token " + {取得したアクセストークン}をつければOKです!!

おわり!!!

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away