はい!今やってます!

Work Pertly, Live Idly

TwitterのOAuthの流れを整理しておく

これはなに

SPAでTwitterのOAuthを実装しようとしていたのだけど、 Twitter Developerで取得できる Consumer API key Consumer API key secret このあたりをどうやって隠蔽するのかというのを考えていたらよくわからなくなってきたので、 基礎知識としてサーバーサイドでTwitter認証する時の流れを整理しておく。 ※Consumer API keyは過去にConsumer tokenと呼ばれていたらしい。今は名前が変わった。

TwitterのOAuthの流れ

TwitterのDeveloperアカウントを設定して払い出されるConsumer API key,Consumer API key secretを使って、 任意のユーザーがAPIを呼び出すためのaccess_token,access_token_secretを得るのがゴール

流れは下記のとおり

  1. Consumer API key,Consumer API key secretを使ってrequest_tokenを得る
  2. https://api.twitter.com/oauth/authenticate?oauth_token={$request_token}に遷移
  3. oauth_tokenoauth_verifierを使ってアクセストークンを発行する

1. request_tokenを得る

まずはrequest_tokenを得る これはConsumer API key,Consumer API key secretを使って、oauth/request_tokenにPOSTリクエストを投げると取得することができる。 ちなみにoauth_callbackというパラメーターにTwitterDeveloperのcallbackに指定したURLのいずれかを設定してリクエストを送信する必要がある。

2. request_token認証を使って認証画面に遷移する

前の手順で得られたrequest_tokenoauth_tokenパラメーターにつけて

https://api.twitter.com/oauth/authenticate?oauth_token={$request_token}

というURLにアクセスすると、Twitter側の提供されるログイン画面に遷移する。 ※oauth_token={$request_token}がすでに気持ち悪いが気にしない。

3. アクセストークンを発行する

連携画面でログインが成功すると、callbackで指定したURLに遷移する。 遷移時のGETパラメーターに

oauth_token={$oauth_token}&oauth_verifier={$oauth_verifier}

という形でパラメーターが付与されるので、 Consumer API key,Consumer API key secret,oauth_token, oauth_verifierをつかって oauth/access_tokenに対してPOSTのリクエストを投げる。 するとaccess_tokenaccess_token_secretが得られる。

複雑なように見えるが実際は三度の通信が走るだけになっている。

※なおTwitterのOAuthはシグネチャを生成して、リクエストに含める必要がある。 リクエストパラメーターやtokenをひとまとめにしたデータを先性して、パラメーターを送信し整合性チェックを行う設計らしい。

ファイル末尾に自動的に改行文字を挿入する(VSCode)

VSCodeを利用していて、ファイルの末尾に自動的に改行コードを加えたくなるときがありました。 具体的にはSchema作成用のSQLファイルを作っていると、最後に改行文字が必要なのですが、 大体これを入力し忘れる。

自分はメインのエディタがVSCodeなので、VSCodeで末尾に自動的に改行文字を挿入する設定をまとめました。

  • VSCodeのPreferenceを開く

    f:id:yuji_ueda:20190425182452p:plain
    VSCodeのPreferenceから設定を開く

  • 設定画面でfiles.insertFinalNewlineで検索

    f:id:yuji_ueda:20190425182718p:plain
    files.insertFinalNewline

files.insertFinalNewlineで設定を検索して、こちらの設定をONにしてください。 jsonで設定ファイルを記述する場合は"files.insertFinalNewline": trueです。

以上。

LaravelでAjaxリクエストかFormリクエストかどうかは誰が判定しているか

LaravelでAPIを書いていて、適当なRestClientを使ってリクエストを送った際に、 Validationエラーで引っかかると、HTTPステータスコード302でリダイレクトされるということがあった。 APIなので、単純にValidationのエラー結果をjsonなりで返してくれればいいのだけどHTTPヘッダーが不足していた。

FormリクエストだとValidationに引っかかった時に、もとのページにリダイレクトして、 Ajax通信の場合はjsonを返すという仕様は公式ドキュメントにも書かれている。

AJAXリクエストとバリデーション この例ではアプリケーションにデータを送るために伝統的なフォームを使いました。しかし、多くのアプリケーションでAJAXリクエストが使用されています。AJAXリクエストにvalidateメソッドを使う場合、Laravelはリダイレクトレスポンスを生成しません。代わりにバリデーションエラーを全部含んだJSONレスポンスを生成します。このJSONレスポンスは422 HTTPステータスコードで送られます。

結論から言うと、 X-Requested-WithヘッダーにXMLHttpRequestを渡せば、Ajaxリクエストとして判定してくれてValidationエラーの内容がjsonで返ってくるのだけど、

f:id:yuji_ueda:20190425094349p:plain
X-Requested-With:XMLHttpRequest

少し気になったのでコードを読んでみた。

vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php

    /**
     * Create a response object from the given validation exception.
     *
     * @param  \Illuminate\Validation\ValidationException  $e
     * @param  \Illuminate\Http\Request  $request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function convertValidationExceptionToResponse(ValidationException $e, $request)
    {
        if ($e->response) {
            return $e->response;
        }

        return $request->expectsJson()
                    ? $this->invalidJson($request, $e)
                    : $this->invalid($request, $e);
    }

vendor/laravel/framework/src/Illuminate/Http/Concerns/InteractsWithContentTypes.php

    /**
     * Determine if the current request probably expects a JSON response.
     *
     * @return bool
     */
    public function expectsJson()
    {
        return ($this->ajax() && ! $this->pjax() && $this->acceptsAnyContentType()) || $this->wantsJson();
    }

vendor/laravel/framework/src/Illuminate/Http/Request.php

    /**
     * Determine if the request is the result of an AJAX call.
     *
     * @return bool
     */
    public function ajax()
    {
        return $this->isXmlHttpRequest();
    }

vendor/symfony/http-foundation/Request.php

    /**
     * Returns true if the request is a XMLHttpRequest.
     *
     * It works if your JavaScript library sets an X-Requested-With HTTP header.
     * It is known to work with common JavaScript frameworks:
     *
     * @see http://en.wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
     *
     * @return bool true if the request is an XMLHttpRequest, false otherwise
     */
    public function isXmlHttpRequest()
    {
        return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
    }

こんな感じ。