tag:crieit.net,2005:https://crieit.net/tags/react-admin/feed 「react-admin」の記事 - Crieit Crieitでタグ「react-admin」に投稿された最近の記事 2020-08-30T22:58:17+09:00 https://crieit.net/tags/react-admin/feed tag:crieit.net,2005:PublicArticle/16040 2020-08-30T22:58:17+09:00 2020-08-30T22:58:17+09:00 https://crieit.net/posts/GitHub-OAuth-App GitHub OAuth Appのトークンを取得失敗した <h1 id="access tokenはクライアント側から取得できない"><a href="#access+token%E3%81%AF%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E5%81%B4%E3%81%8B%E3%82%89%E5%8F%96%E5%BE%97%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84">access tokenはクライアント側から取得できない</a></h1> <h2 id="やりたかったこと"><a href="#%E3%82%84%E3%82%8A%E3%81%9F%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8">やりたかったこと</a></h2> <p>GitHubをJSONデータベース代わりに使いたかった. GitHub APIを使うとGitHubをREST APIから操作できるので, これにフロント付けたら簡易なデータベースを作れるのではと思ったわけです. これは結局Cross-Origin Request Blockedでできないことが分かりました.</p> <p>忘れちゃうのもあれなので概要をメモしておきます.</p> <h2 id="環境"><a href="#%E7%92%B0%E5%A2%83">環境</a></h2> <p>以下のパケージを利用しました.</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://nextjs.org/">Next.js</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://marmelab.com/react-admin/">react-admin</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/octokit/auth-oauth-app.js">octokit/auth-oauth-app.js</a></li> </ul> <p>またreact-adminで認可するロジックは以下を参考にしました.</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/marmelab/ra-example-oauth">ra-example-oauth</a></li> </ul> <h2 id="OAuth Applicationの作成"><a href="#OAuth+Application%E3%81%AE%E4%BD%9C%E6%88%90">OAuth Applicationの作成</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://docs.github.com/en/developers/apps/creating-an-oauth-app">Creating an OAuth App</a>を参考にしましょう.</p> <p>作成されたGitHub OAuth Appには固有の値であるclient idとclient secretが付与されるます.</p> <h2 id="Auth Provider"><a href="#Auth+Provider">Auth Provider</a></h2> <p>react-adminで認証や認可を行うにはauth providerにロジックを書きます.</p> <pre><code class="javascript">const authProvider = { login: params => Promise.resolve(), logout: params => Promise.resolve(), checkAuth: params => Promise.resolve(), checkError: error => Promise.resolve(), getPermissions: params => Promise.resolve(), }; </code></pre> <p>取得したトークンはローカルストレージに保存します. checkAuthではこのトークンがすでに設定されているかを確認し, 無ければログイン画面が表示されます.</p> <pre><code class="javascript">checkAuth: () => { const user_name = localStorage.getItem('username'); console.log(`checkAuth: ${user_name}`) return localStorage.getItem('username') ? Promise.resolve() : Promise.reject(); }, </code></pre> <p>ログイン画面では2つの処理を行います. まず認可ページで認可を行います. 認可後はAuthorization callback URLに指定したページに戻ってくるのですが, この際codeというURLパラメータが付与されます. これとclient id, client secretを使ってトークンを取得します. stateは任意ですが一応付けてます.</p> <pre><code class="javascript">import { createOAuthAppAuth } from '@octokit/auth-oauth-app'; const authorizeURL = new URL('oauth/authorize', process.env.login_url); authorizeURL.searchParams.append('client_id', process.env.client_id); authorizeURL.searchParams.append('scope', 'public_repo'); authorizeURL.searchParams.append('state', 'QAbgG6f9Rit2C5'); const auth = createOAuthAppAuth({ clientId: process.env.client_id, clientSecret: process.env.client_secret, }); login: async (params) => { const { searchParams } = new URL(window.location.href); if (!searchParams.has('code') || !searchParams.has('state')) { redirectToURL(authorizeURL) return Promise.resolve(); } const code = searchParams.get('code'); const state = searchParams.get('state'); const tokenAuthentication = await auth({ type: 'token', code: code, state: state, }); const { token } = tokenAuthentication; localStorage.setItem('username', token); return Promise.resolve(); }, </code></pre> <p>ところがauthでtokenを取得しようとすると失敗します. リバース・プロキシを設置してやるといけるようですが, 試してません.</p> <p><a target="_blank" rel="nofollow noopener" href="https://andreybleme.com/2018-02-24/oauth-github-web-flow-cors-problem/">OAuth Github web flow doesn't support CORS</a></p> <h2 id="補足"><a href="#%E8%A3%9C%E8%B6%B3">補足</a></h2> <p>Next.jsとreact-adminを使うとDocument is not definedというエラーが出ます. hisotryというパッケージが内部でDOM APIを利用しているのですがNext.jsは最初ローカル側(サーバー側)で動くのでこの時点ではDOMが存在しません. クライアント側かどうか調べる必要があります. getStaticPropsはロカールで実行され, ファイルなどを取得してpropsとしてページ・コンポーネントに渡すことができます. この処理が終了するとブラウザ側の処理に移ることを利用するとコンポーネントを切り替えることができるようです.</p> <p><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/58987174/how-to-prevent-parent-component-from-re-rendering-with-react-next-js-ssr-two-p">How to prevent parent component from re-rendering with React (next.js) SSR two-pass rendering?</a></p> ブレイン