shinonome-inc / qiita_client_yo

【模擬開発案件】Qiitaクライアントアプリ(PlayGroundモバイルコース最終課題)
3 stars 0 forks source link

【質問】QiitaAPIにおけるAuthorizationリクエストヘッダの利用方法について #22

Closed KobayashiYoh closed 2 years ago

KobayashiYoh commented 2 years ago

概要

お世話になっております。 QiitaAPIにおけるAuthorizationリクエストヘッダの利用方法について質問です。

現在、QiitaAPIを利用したアクセストークンの発行まで実装が完了しております。 QiitaAPIのドキュメントを見て、発行したアクセストークンを基にAuthorizationリクエストヘッダが作成できることは理解できました。 しかし、そのAuthorizationリクエストヘッダをどうのようにして利用すればよいのかがわかりません。

Authorizationリクエストヘッダの利用方法を教えていただけないでしょうか。 ご回答よろしくお願いします。

実装したい内容

Authorizationリクエストヘッダを利用してFeed PageやTag Pageで1時間に1000回までリクエストを送れるようにする

考察

QiitaAPIにおけるAuthorizationリクエストヘッダが Authorization: Bearer <発行したアクセストークン> の形式で表されることはわかりました。 クエリと同様に、リクエストのURLに組み込めば使えるのではないかという考えが浮かんだのですが、どのように組み込んだらよいのかがわかりませんでした。

KobayashiYoh commented 2 years ago

クエリと同様に、リクエストのURLに組み込めば使えるのではないかという考えが浮かんだのですが、どのように組み込んだらよいのかがわかりませんでした。

下記のサイトでHTTP通信におけるAuthorizationのリクエストヘッダについて調べ直してみました。 リクエストヘッダというのは、URLに組み込むものではなさそうですね。 https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Authorization

KobayashiYoh commented 2 years ago

…ということは、URLとリクエストヘッダを一緒にするのではなく、それぞれ別々にしてリクエストに含めてあげる必要がありそうですね。 そうなってくると、鍵になるのはFlutterのhttpパッケージでしょうか。 https://pub.dev/packages/http

KobayashiYoh commented 2 years ago

現状

httpパッケージのgetメソッドは、urlだけでなく、headerも指定できることがわかりました。 そこで、http.getのheaderにリクエストヘッダを指定してFeedPageの記事を取得しようとしたのですが、できませんでした。 responseコードすら返ってこないのですが、headerの書き方が正しくないのでしょうか。

// 省略

class Client {
  static Map<String, String>? _authorizationRequestHeader = {
    'Authorization': '{Authorization: Bearer $Variables.accessToken}',
  };

  // QiitaAPIで記事を取得
  static Future<List<Article>> fetchArticle(
      int currentPageNumber, String searchWord) async {
    var url = searchWord.isEmpty
        ? 'https://qiita.com/api/v2/items?page=$currentPageNumber'
        : 'https://qiita.com/api/v2/items?page=$currentPageNumber&query=$searchWord';

    var response = Variables.accessToken.isNotEmpty
        ? await http.get(
            Uri.parse(url),
            headers: _authorizationRequestHeader,
          )
        : await http.get(
            Uri.parse(url),
          );

    if (response.statusCode == 200) {
      final List<dynamic> jsonResponse = json.decode(response.body);
      return jsonResponse.map((json) => Article.fromJson(json)).toList();
    } else {
      throw Exception('Request failed with status: ${response.statusCode}');
    }
  }
  // 省略
}
ShuheiYoshidaJP commented 2 years ago

headerのkeyをAuthorization valueをBearer xxxxxxxxで実験してみて下さい。

KobayashiYoh commented 2 years ago

headerのkeyをAuthorization valueをBearer xxxxxxxxで実験してみて下さい。

headerを下記のように変更してみたのですが、記事の取得はできませんでした。 書き方に問題があるのでしょうか。

  headers: {
     'Authorization': 'Bearer xxxxxxxx',
  },
ShuheiYoshidaJP commented 2 years ago

API通信系で不具合がある場合は

  1. Postmanでレスポンスが返ってくるか確認
  2. デバックモードでresponseの中身を見てみる

の方法があります

KobayashiYoh commented 2 years ago

まずはデバッグモードを試してみます。

KobayashiYoh commented 2 years ago

responseの中身を確認するデバッグ(1回目)

コードの21行目をブレークポイントとしてデバッグを行ったところ、responseがnullになっていることが判明しました。 headerを利用せずにAPIで記事を取得する際はこのような現象は起こったことがないため、今回新たに実装した内容を見直していこうと思います。

デバッグのブレークポイント

21    var response = Variables.accessToken.isNotEmpty
22        ? await http.get(
23             Uri.parse(url),
24             headers: {
25               'Authorization':
26                   'Bearer <アクセストークン>',
27            },
28          )
29        : await http.get(
30            Uri.parse(url),
31          );


デバッグの結果

スクリーンショット (415)

KobayashiYoh commented 2 years ago

responseの中身を確認するデバッグ(2回目)

上記のデバッグはブレークポイントが不適当だったため、ブレークポイントを変更して再びデバッグを行いました。 その結果、responseはnullではありませんでした。

ブレークポイント

if (response.statusCode == 200) 


デバッグ結果

ログインしてアクセストークンを発行した場合(Variables.accessToken.isNotEmpty == true) スクリーンショット (416)

ログインせずアクセストークンを発行しなかった場合(Variables.accessToken.isNotEmpty == false) スクリーンショット (417)


考察

アクセストークンを発行して利用しようとしたとき、responseは statusCode = 401, resonPhrase = "Unauthorized" となりました。 記事を取得するには、responseをアクセストークン未発行時と同じ statusCode = 200, resonPhrase = "OK" となる必要があると思いました。 まずはstatusCode = 401がどういう意味を持つのか調べてみようと思います。

KobayashiYoh commented 2 years ago

【質問】401エラーについて

先ほどのデバッグの結果、アクセストークン発行時のresponse.statusCodeが401となっていることが判明しました。 そのstatusCode = 401について調べた結果、認証資格が不足しているということは理解できました。 しかし、具体的にどこがどのように不足しているのかは理解できませんでした。 不足していると思われる認証資格とは何なのか教えていただけますでしょうか。

参考:https://developer.mozilla.org/ja/docs/Web/HTTP/Status/401

mcz9mm commented 2 years ago

どこまで実装ができてるかわからないので実装してるブランチ名とenv情報を記載して教えてください

KobayashiYoh commented 2 years ago

どこまで実装ができてるかわからないので実装してるブランチ名とenv情報を記載して教えてください

わかりました。 少々お待ちください。

ShuheiYoshidaJP commented 2 years ago

トークン情報が正しいかわからないので 先日紹介したPostman使ってみて下さい。

KobayashiYoh commented 2 years ago

pushしました。 実装してるブランチ名は"feature/top"です。 ClientIDとClientSecretはenvファイルではなく、dartファイルに書いてモバイルコースのGoogleDriveに保存しました。

ブランチ:https://github.com/shinonome-inc/mobile_yo/tree/feature/top ClientIDとClientSecretのダウンロード:https://drive.google.com/file/d/1yv6hG-jKviItMLHo928iLZKPUp1HoijL/view?usp=sharing

KobayashiYoh commented 2 years ago

トークン情報が正しいかわからないので 先日紹介したPostman使ってみて下さい。

わかりました。 使ってみます。

KobayashiYoh commented 2 years ago

Postmanを用いたresponseの検証

検証のためにPostmanを使ってレスポンスを確認したのですが、うまく認証ができていませんでした。 Postmanを使って認証ができないということは、Flutterの書き方がどうというよりも、トークン情報が間違っているという認識で合っていますでしょうか。

レスポンス

{
    "message": "Unauthorized",
    "type": "unauthorized"
}


スクリーンショット

スクリーンショット (418)_LI

ShuheiYoshidaJP commented 2 years ago

トークン情報が間違っている可能性が非常に高いですね。

KobayashiYoh commented 2 years ago

トークン情報が間違っている可能性が非常に高いですね。

やっぱり、その可能性が高いですよね。 トークン発行のプロセスを見直してみます。

KobayashiYoh commented 2 years ago

トークン情報が間違っている原因

トークン情報が間違っているのは、自分の勘違いが原因でした。 今まで自分は「リダイレクト先URLのcode」を「アクセストークン」だと勘違いしていました。 https://community.4nonome.com/@yo/107693082555511781

POST /api/v2/access_tokensを使って真のアクセストークンを発行していきたいと思います。 自分の勘違いによってご迷惑をおかけしまい申し訳ありません。

参考: https://sanposhiho.com/posts/2020-01-12-qiita-f827934c58f4ee8dab63/ https://qiita.com/api/v2/docs#post-/api/v2/access_tokens

mcz9mm commented 2 years ago

この code の値を利用して [POST /api/v2/access_tokens](https://qiita.com/api/v2/docs#post-/api/v2/access_tokens) にリクエストを送り、アクセストークンを発行します ここに書いてあるのしっかり読もう。ドキュメントは大事なことに気づけたと思うので良い経験じゃないかなと思います。

アクセストークンを発行するには、アプリケーションのユーザに認可画面を表示する必要があります。ユーザがアプリケーションからのアクセスを認可すると、アプリケーション登録時に指定されたURLにリダイレクトされます。このとき、リダイレクト先のURLクエリにcodeが付与されます。また指定した場合は state も付与されます。アプリケーションでは、この code の値を利用して POST /api/v2/access_tokens にリクエストを送り、アクセストークンを発行します。

KobayashiYoh commented 2 years ago

読んでわかった"つもり"になっていました。 時間はかかってしまいましたが、良い経験になったかなと思っています。 この失敗をこれからの実装に活かしていきたいです。