1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

QiitaAPIAdvent Calendar 2024

Day 20

Qiita OAuth API を使ってコマンドを叩くだけでQiita APIトークンを取得する

Last updated at Posted at 2024-12-22

Qiita CLI

導入

Qiita公式がCLI上からQiitaに記事を投稿できるQiita CLIというツールを公開しています。
qiita-cliについて詳しくはこちらを参照してください

qiita-cliのインストール後の初期設定のために以下のコマンドを実行します

npx qiita login

実行後APIトークンの入力を求められます
APIトークンこちらより新しく発行するか発行済みの値を入力して設定します

所感と課題

...APIトークンの入力面倒くさくないですか?
APIトークンの値とか失念しているでしょうし、新しくAPIトークンをつくるのは面倒くさいですし...
このような初期設定の面倒臭さはqiita-cliのユーザー初心者にとってライブラリを使用する上での敷居の高さにつながっているように思います

他の事例

上記のような点を課題と感じたきっかけとして他のCLIツールでは API Key の入力を求められることなく利用可能になるように作られているものが多く存在あります。
例えばGoogle App Scriptをローカルで開発するツールであるclaspを使用した場合のインストール後の初期設定のコマンドは以下の通りになります。

clasp login

上記のコマンドを実行するブラウザがたちあがり以下のような画面が表示されます

clasp-login1.png

「次へ」を押すと以下の画面が表示されます

clasp-login-permissions.png

認可してもいい権限すべてをチェックして以下のように「続行」を押します

clasp-login-permissions-to-next.png

「続行」を押した後はOAuth認可が完了し、これでclaspの各種コマンドが実行できるようになります
clasp だけでなくfirebase-toolsなど、他のCLIツールにおいても初期設定時には同様の推移をたどることで初期設定をすることができます
qiita-cliにおいても同様なでも実現できたらいいなぁ...

claspの事例を参考にする

qiita-cliでもclaspと同様のインターフェースを実現させるためにclaspではどのように実現しているか参考にする。
とりあえずclaspにいて該当のソースコードが↓であるので見てみます。

auth.ts

詳しくはソースコードを見てください。
ソースコードより以下のことをやっていることが読み取れます

  1. CLIよりローカルサーバーを立ち上げる
  2. CLIよりGoogle OAuthの認可画面を開く
  3. 認可した時にローカルに戻ってくる (http://localhost)
  4. 戻ってきたときにOAuth tokenの情報を取得して、その情報をマシンに記録する

以上のことを行っており、Google OAuth APIを使用しているがローカルの中だけで完結していることが読み取れます。
(またclaspにはGoogle OAuthに関する情報がすべてべた書きされている。事前にOAuth認可の戻り先のURLに http://localhost を指定していると思われる)
なおclaspの実装内容を パク 参考にしてCLIの中だけでGoogle APIを使用することを実装して実現できたことを確認しました。
以下は確かめてみて実現できたプロジェクトです。(開発中につき解説などについては後日もう少し整理できてから行います)

google-photos-cli

またこのプロジェクトでは以下のようにOAuth認可の戻り先のURLに http://localhost としてあらかじめ指定することでlocalhostに戻せることができることを確認しました。(ちなみにGoogleのOAuth APIの場合は http://localhost と指定することでポート番号の区別なく(どのポート番号に)戻ることができます)

google-oauth-api-callback.png

qiita-cliにおいてもclaspと同様のフローでのログインをするためには以下のことが実現可能である必要があります

  1. Qiita OAuth APIを利用できること
  2. Qiita OAuth APIの戻り先のURLにローカルホスト(http://localhost)を指定することができること

Qiita Oauth API

Qiita APIには認証認可(OAuth)の機能も存在します
導入方法についてはこちらのドキュメントを参考に導入していきます

Qiita API v2#認証認可

とりあえずこのQiita Oauth APIを使ってCLIの中だけでQiita APIトークンを取得することを試みていきたいと思います。

Qiita OAuth APIの登録

ユーザーの管理画面 を選択し「アプリケーションを登録する」を選択してQiita OAuth APIを使用するための登録を行います

qiita-application-list.png

各種情報の入力します、この時「リダイレクトのURL」の部分には http://localhost:ポート番号 を指定して登録します。
※ Qiita OAuth APIではlocalhostのURLを指定することができましたが、ポート番号を指定する必要があります

qiita-regist-token.png

無事に登録が完了すると Client IdClient Secret の値が取得できるので実際に実装するときに使用するのでこの値を控えておきます。

CLIからQiita OAuth APIを介して実際にQiita API tokenを取得する

実際にNode.jsを使ってCLIを実行してローカルの中だけでQiita OAuth APIを介してQiita API Tokenを取得する処理を以下に記述します。

const qiitaOauthClientTokens = {
  clientId: 'QiitaOAuthAPIClientId',
  clientSecret: 'QiitaOAuthAPIClient Secret'
};

// OAuth認可を行う時に受け取るサーバのポート番号
const serverPortNumber = 59116;

async function startLocalServer(): Promise<Server> {
  return new Promise<Server>((resolve) => {
    const server = createServer();
    enableDestroy(server);
    server.listen(serverPortNumber, () => resolve(server));
  });
}

async function recieveOauthCallbackCode(server: Server): Promise<string> {
  return new Promise<string>((resolve, reject) => {
    server.on(
      'request',
      (
        request: ReadonlyDeep<IncomingMessage>,
        response: ReadonlyDeep<ServerResponse>
      ) => {
        const urlParts = new URL(request.url ?? '', 'http://localhost')
          .searchParams;
        const code = urlParts.get('code');
        const error = urlParts.get('error');
        if (code) {
          resolve(code);
        } else {
          reject(error);
        }
        response.setHeader('Content-Type', 'text/plain;charset=utf-8');
        // OAuth認可から戻ってきたときにブラウザ画面に表示させる内容
        response.end('codeの取得が完了しました。ブラウザを閉じてください');
      }
    );
  });
}

export async function oauthLogin(): Promise<string> {
  const server = await startLocalServer();
  // OAuth認可を行う時にローカル確認を行うためのstateを指定する必要があるのでてきとうな値を作る
  const authorizeState = crypto.randomBytes(12).toString('hex');
  const permissionScope = [
    'read_qiita',
    'write_qiita',
    'read_qiita_team',
    'write_qiita_team',
  ].join('+');
  const qiitaAuthorizeUrl = `https://qiita.com/api/v2/oauth/authorize?client_id=${qiitaOauthClientTokens.clientId}&scope=${permissionScope}&state=${authorizeState}`;
  await open(qiitaAuthorizeUrl);
  const authCode = await recieveOauthCallbackCode(server).finally(() => {
    server.destroy();
  });
  const accessTokenResponse = await getAccessToken(
    qiitaOauthClientTokens.clientId,
    qiitaOauthClientTokens.clientSecret,
    authCode
  );
  return accessTokenResponse.data.token;
}

※上記処理を実行するために使用しているライブラリは以下の通りです。事前にyarn addnpm install --saveをして導入しておいてください

上記の処理を記述後以下のようにfunctionを呼び出します。

const token = await oauthLogin();

このようにCLIの中で呼び出すことで途中で以下のようにブラウザが立ち上がり「許可する」をおすと token にQiita API tokenを取得できます

qiita-oauth-confirm.png

あとはこの token をローカルファイルに記録するなりして活用することでQiita APIを実行することができます。

実は...

Qiita 公式がqiita-cliを公開するより前に私も非公式でqiita-cliというプロジェクトがありその開発(のお手伝い)をしていました。
こちらがForkして継続して開発していた私のプロジェクトとなります。
※なお Qiita OAuth API を利用するための Client IdClient Secret の値は clasp 同様にソースコードにべた書きしていますが、ライブラリとして配布する場合はべた書きで公開状態でも問題ないと思われます。結局はローカル内で完結する話なので。

元々開発していた方が紹介している記事

Qiita記事管理CLI(qiita-cli)をTypeScriptでつくってみた

その中で今回のQiita OAuth APIを使ってCLIの中だけでログインすることができるのかどうかを検証していました。¥・
検証の結果実現できたのですが私の開発意欲の低下に伴い、放置となっ¥てしまいました
そして気が付いたらQiita公式がqiita-cliを作って公開したという状況となっていました。
役割を終えた形となりました。(無事、供養されると幸いです…)

公式のqiita-cliにてこのQiita OAuth APIを使ってのログイン機能の実装の導入のお手伝いをするかどうかについては依頼が来たらやろうかな...と考えています

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?