105
91

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rails×Next.js×Auth0で認証機能をサックと開発する

Last updated at Posted at 2022-07-15

はじめに

今回はRailsのAPIモードとNext.jsを利用したSPA構成に、Auth0で認証機能をサクッと開発していきます。

Auth0は導入が簡単かつチュートリアルやドキュメントが豊富なので、初めての人でも簡単に認証機能を実装することができます。

直近で自分が行った新規開発案件でもAuth0が採用されることが多かったです。

なおAuth0についての詳しい解説は本記事では割愛するので気になった方は、こちらを確認してみてください。

またAuth0とFirebase Authenticationといった他の認証機能との比較記事はこちらで確認してみてください。

本記事で解説する内容はAuth0の公式ドキュメントに沿って解説していきます。

この記事の対象者

  • Next.js×Auth0で認証機能を開発したい
  • RailsのAPIモードでAuth0の認証機能を開発したい
  • Rails×Next.jsで認証機能(新規登録・ログイン・ログアウト)を開発したい
  • 手を動かしながらAuth0を理解したい
  • SPAで認証機能を持つアプリを開発したい

フロント側(Next.js側)の開発

まずはフロント側でのAuth0の実装をしていきます。

Next.jsプロジェクトの環境構築

nodeやyarnの環境構築されているものとして進めていきます。

またフロント側はauth0-front、バックエンド側はauth0-backという名前でプロジェクトを作成します。(これは任意です)

さっそく下記のコマンドでNext.js×TypeScriptの環境を作成します。

npx create-next-app auth0-front --typescript

終わったらdev環境を立ち上げます

npm run dev   

こちらが表示されていたら、Next.js×TypeScriptのプロジェクトの環境構築は完了です。

スクリーンショット 2022-07-11 18.27.40.jpg

Auth0でのプロジェクト作成

次にAuth0側での設定を進めていきます。

こちらは公式ドキュメントを元に解説を進めていきます。

まず初めにAuth0の公式サイトへアクセスしGoogleログイン等でAuth0へのアカウント作成を行います。

今回は個人で開発として進めるのでPersonalを選択します。

スクリーンショット 2022-07-11 18.31.00.jpg

次に「+Create Application」からAuth0を利用する新規にプロジェクトを作成します。

スクリーンショット 2022-07-11 18.32.27.jpg

開発するアプリ名を入れて、Single Page Web Applicationsを今回は選択します。

スクリーンショット 2022-07-11 18.33.21.jpg

次にsettingより「Domain」「Client ID」を確認しておきます。

こちらは後でNext.jsの環境変数として設定します。

auth0-app.png

コールバック用のURLを設定する

setting画面で下にスクロールすると「Application URIs」という項目が出てくるので該当の箇所にURLを入力していきます。

はじめにAuth0での認証完了後にユーザーのリダイレクト先を設定します。

なおフロント側のポート番号は9000、バックエンド側は3000で設定します。(設定は後で行います)

http://localhost:9000/login

スクリーンショット 2022-07-14 9.58.04.jpg

このように設定することでフロント側でAuth0経由でのログイン処理完了後にhttp://localhost:9000/loginのページへ遷移することができます。

※ ログイン後なのでhttp://localhost:9000/home等で設定した方が見やすかったかもしれないです。(追記)

実際の遷移先ページの実装はこの後やります。

ログアウト用のURLを設定

こちらも同様に以下のURLを設定します。

http://localhost:9000/

Allowed Web OriginsとAllowed Origins (CORS)

最後にAllowed Web OriginsとAllowed Origins (CORS)の2つを設定します。

こちらも今までと同様に以下のURLを入れます。

http://localhost:9000/

スクリーンショット 2022-07-14 10.02.13.jpg

以上でAuth0側での設定は完了です。

Next.jsでのAuth0の準備

次にNext.jsと今準備をしたAuth0の連携を進めていきます。

SDKをインストール

はじめにAuth0 React SDKをインストールするために以下のコマンドを実行します。

npm install @auth0/auth0-react

Auth0Providerコンポーネントを設定する

Auth0 React SDKではReact Contextを利用することで認証状態を管理することができます。

先程インストールした@auth0/auth0-reactからAuth0ProviderimportしてNext.jsアプリをラップします。

またその際に先程Auth0のsetting画面で確認した「domain」「client_id」を環境変数に定義しておきます。(後述)

process.env["NEXT_PUBLIC_AUTH0_AUDIENCE"]の値はRails側の環境構築完了後に.envに記載するので一旦空の値になっていても大丈夫です。

pages/_app.tsx
import "../styles/globals.css";
import { Auth0Provider } from "@auth0/auth0-react";

function MyApp({ Component, pageProps }) {
  //ログイン後のリダイレクト先を指定
  const redirectUri = `${process.env["NEXT_PUBLIC_BASE_URL"]}/login`;
  return (
    <Auth0Provider
      domain={process.env["NEXT_PUBLIC_AUTH0_DOMAIN"]!}
      clientId={process.env["NEXT_PUBLIC_AUTH0_CLIENT_ID"]!}
      audience={process.env["NEXT_PUBLIC_AUTH0_AUDIENCE"]!}
      redirectUri={redirectUri}
    >
      <Component {...pageProps} />
    </Auth0Provider>
  );
}

export default MyApp;

環境変数は下記のように設定をします。

xxxxには先程Auth0のsetting画面で確認した値を入れてください。

.env
NEXT_PUBLIC_BASE_URL='http://localhost:9000'
NEXT_APP_REST_URL="http://localhost:3000/api/v1"
NEXT_PUBLIC_AUTH0_DOMAIN='xxxxxxxxxxxxxxxxx'
NEXT_PUBLIC_AUTH0_CLIENT_ID='xxxxxxxxxxxxxxxxx'

ポート番号を変更する

最後にNext.js側のポート番号を9000に変更します。

package.jsonファイルのdevを以下の変更することでポート番号を9000することができます。

package.json
  "scripts": {
    "dev": "next dev -p 9000",
    "build": "next build",
  },

実際に確認します。

npm run dev

ポート番号が9000のローカル環境が立ち上がることが確認できるかと思います。

以上でNext.jsアプリでAuth0を使う準備が完了しました。

ログイン機能を作成する

ログイン後に遷移するページを作成する

pages/login.tsx
import { useAuth0 } from "@auth0/auth0-react";
import { NextPage } from "next";
import React from "react";

const LoginPage: NextPage = () => {
  const { isAuthenticated } = useAuth0();

  return (
    <div>
      <h2>ログイン状態</h2>
      {isAuthenticated ? <p>ログイン中です</p> : <p>ログアウトしています</p>}
    </div>
  );
};

export default LoginPage;

useAuth0isAuthenticatedでログイン状態なのかを判定してくれます。

現状はログインをしていないのでlocalhost:9000/loginにアクセスした時に、「ログアウトしています」と表示されることが確認できます。

Homeページにログイン機能を追加する

次にログイン機能を実装します。

pages/index.tsx
import { useAuth0 } from "@auth0/auth0-react";

export default function Home() {
  const { loginWithRedirect } = useAuth0();

  return (
    <div>
      <button onClick={() => loginWithRedirect()}>ログイン</button>
    </div>
  );
}
npm run dev

これによって、画面にログインボタンが表示されていることが確認できます。

スクリーンショット 2022-07-12 14.23.20.jpg

実際にボタンをクリックすると以下のように Auth0側のログイン画面に遷移することが確認できます。

スクリーンショット 2022-07-12 14.24.14.jpg

Googleアカウントでログインすると以下のような画面が表示されるのでAcceptをすればログイン(新規登録)が完了します。

スクリーンショット 2022-07-12 14.25.04.jpg

完了後は先程作成したlogin画面に遷移していることを確認できるかと思います。

スクリーンショット 2022-07-12 14.36.34.jpg

Homeページにログアウトボタンを追加する

先程作成したpages/login.tsxにおいてログイン中だった場合のみログアウトボタンを表示させログアウト処理が行えるように書き換えます。

pages/login.tsx
import { useAuth0 } from "@auth0/auth0-react";
import { NextPage } from "next";
import React from "react";

const LoginPage: NextPage = () => {
  const { isAuthenticated, logout } = useAuth0();

  return (
    <div>
      <h2>ログイン状態</h2>
      {isAuthenticated ? (
        <>
          <p>ログイン中です</p>
          <button onClick={() => logout({ returnTo: window.location.origin })}>
            ログアウト
          </button>
        </>
      ) : (
        <p>ログアウトしています</p>
      )}
    </div>
  );
};

export default LoginPage;

実際にログアウトボタンをクリックすると文言が変わっていることが確認できます。

スクリーンショット 2022-07-12 15.07.34.jpg

以上でフロント側での新規登録、ログイン、ログアウトの実装が完了しました。

次にRailsのAPIモード側のAuth0の設定をしていきます。

バックエンド側(Rails側)の開発

バックエンド側もフロントと同様にAuth0での設定をした上でRailsアプリ側で実装を進めていきます。

こちらのドキュメントの流れで解説をしていきます。

Auth0側の設定

まずはじめにApplications > APIsからcreate APIをクリックします。

スクリーンショット 2022-07-12 15.23.45.jpg

該当項目を入力しcreateをクリックします。

スクリーンショット 2022-07-12 15.25.50.jpg

ドメイン情報を取得するためTest > urlからドメイン情報を確認します。

urlの.comまでがドメイン情報になっています。

スクリーンショット 2022-07-12 15.27.45.jpg

これでauth0側の設定は完了なのでRailsアプリの設定をしていきます。

APIモードでRailsアプリを環境構築

auth0-backというディレクトリ名でアプリを開発していきます。

rails new auth0-back --api

以下をgemファイルに追加してbundle installをします。

Gemfile
gem 'dotenv-rails'
gem 'jwt'
gem 'rack-cors'
  • dotenv-railsは環境変数を設定するためのgem
  • JsonWebTokenを利用してAccess Tokenの検証をするのでjwtを使う
  • rack-corscorsの設定を行うために使います

CORSについてはこちらを確認してみてください。

環境変数を定義する

先程のAuth0側で確認したdomainidentifierをこちらに格納します。

.env
AUTH0_DOMAIN="xxxxxxxxx"
AUTH0_IDENTIFIER="xxxxxxxxx"

identifierに関してはAuth0の管理画面から確認できます。

スクリーンショット 2022-07-12 15.44.50.jpg

またidentifierの値はフロント側の.envにも後で記載します。

JWTクラスを作成する

こちらはドキュメントの記述をそのまま流用します。

処理内容は公式ドキュメントに書いてある通り、リクエストのAuthorizationのヘッダーから取得したAccess TokenをデコードするJsonWebTokenクラスを作成しています。

これでAuth0テナントの公開鍵を取得し、トークンの検証をすることができる。

app/lib/json_web_token.rb
# lib/json_web_token.rb

# frozen_string_literal: true
require 'net/http'
require 'uri'

class JsonWebToken
  def self.verify(token)
    JWT.decode(token, nil,
               true, # Verify the signature of this token
               algorithms: 'RS256',
               iss: 'https://dev-k4qwtrhp.jp.auth0.com/',
               verify_iss: true,
               aud: Rails.application.secrets.auth0_api_audience,
               verify_aud: true) do |header|
      jwks_hash[header['kid']]
    end
  end

  def self.jwks_hash
    jwks_raw = Net::HTTP.get URI("https://dev-k4qwtrhp.jp.auth0.com/.well-known/jwks.json")
    jwks_keys = Array(JSON.parse(jwks_raw)['keys'])
    Hash[
      jwks_keys
      .map do |k|
        [
          k['kid'],
          OpenSSL::X509::Certificate.new(
            Base64.decode64(k['x5c'].first)
          ).public_key
        ]
      end
    ]
  end
end

認証用のクラスを作成する

認証のロジックを抽出することでJWTの真正性を検証する処理とHTTPリクエストを認証する処理を分離している。

app/services/authorization_service.rb
class AuthorizationService
  def initialize(headers = {})
    @headers = headers
  end

  def current_user
    @auth_payload, @auth_header = verify_token
    @user = User.from_token_payload(@auth_payload)
  end

  private

  def http_token
    @headers['Authorization'].split(' ').last if @headers['Authorization'].present?
  end

  def verify_token
    JsonWebToken.verify(http_token)
  end
end
  • AuthorizationService
    • Authorization HTTP Headerに含まれるアクセストークンを取得しJWTに渡して検証する
  • verify_token
    • json_web_token.rbを実行してTokenを渡す
  • current_user
    • ユーザーモデルメソットを実行し、ユーザー情報を返す

SecuredControllerを作成する

APIエンドポイントのセキュリティー確保を行うsecured_controlleを作成する。

こちらに関しても同様にAuth0のドキュメントを参考に記述をします。

app/controllers/secured_controller.rb
class SecuredController < ApplicationController
  before_action :authorize_request

  private

  def authorize_request
    authorize_request = AuthorizationService.new(request.headers)
    @current_user = authorize_request.current_user
  rescue JWT::VerificationError, JWT::DecodeError
    render json: { errors: ['Not Authenticated'] }, status: :unauthorized
  end
end

SecuredControllerクラスhaApplicationControllerを継承しており、全てのコントローラー実行前にauthorize_requestを実行します。

authorize_requestではtokneを解析し、ユーザー認証ができなかった場合はエラーメッセージを返すようなメソットです。

最後にParameter Wrappingを無効にするために以下のような記述をします。

config/initializers/wrap_parameters.rb
wrap_parameters format: []

以上でRails側でのAuth0の準備は完了しました。

モデルを作成する

次にAuth0を利用してユーザーモデルを作成します。

今回はブログ投稿型のアプリを開発するというて程で、Auth0の認証機能を使っていきます。

実際に行う内容は下記の内容です

  • ブログの新規作成は認証済みユーザーだけができる
  • ブログの閲覧は認証していないユーザーもできる

ユーザーモデルの作成

まずは以下のコマンドでユーザーモデルを作成します。

rails g model User

マイグレーションファイルを以下のように書きます。

20220712073809_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :users do |t|
      t.string :sub, null: false
      t.timestamps
    end
  end
end

subはユーザーidが入ってきます。

またユーザーとブログ記事は1:Nの関係でリレーションが結ばれているので、ユーザーモデルを下記のように書き換えます。

app/models/user.rb
class User < ApplicationRecord
  has_many :posts, dependent: :destroy

  def self.from_token_payload(payload)
    find_by(sub: payload['sub']) || create!(sub: payload['sub'])
  end
end

self.from_token_payload(payload)ではtoken情報を参照して対象のuserが存在する場合はuser情報を返し、存在しない場合は新規作成の処理を行いまう。

ブログ記事のモデルを作成する

次にブログ記事のモデルを作成していきます。

rails g model Post user:references title:string caption:text

マイグレーションを実行します。

rails db:migrate

ブログ記事のコントローラーを作成

  • ブログの取得(index,show)は認証なしで実行できる
  • ブログの新規作成は認証がないと実行できない
  • ブログの削除も認証がないと実行ができない

authorize_requestの処理はapp/controllers/secured_controller.rbに記述済み。

app/controllers/api/v1/posts_controller.rb
class Api::V1::PostsController < SecuredController
  skip_before_action :authorize_request, only: [:index,:show]

  def index
    posts = Post.all
    render json: posts
  end

  def show
    post = Post.find(params[:id])
    render json: post
  end

  def create
    # ユーザー認証
    post = @current_user.posts.build(post_params)

    if post.save
      render json: post
    else
      render json: post.errors, status: :unprocessable_entity
    end
  end

  def destroy
    post = Post.find(params[:id])
    post.delete
  end

  private

  def post_params
    params.permit(:title,:caption)
  end
end

skip_before_actionでindexとshowメソットのみ認証処理をスキップしている。

ルーターの設定

config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :posts
    end
  end
end

Postmanを利用してAPIコールを試す

Postmanの準備に関しては割愛します。まだの方はこちらを参考に準備をしてください。

トークンを入れずに新規作成(POST)を試す

はじめにトークンを入れずにPostmanでブログ記事の新規作成を試します。

Railsのサーバーを立ち上げます。

rails s
  • POSTを選択肢してhttp://127.0.0.1:3000/api/v1/を入れます
  • Paramsを選択肢titlecaptionに値を入れます
  • その上でSendボタンを押します
  • トークンを入れていないので結果として"Not Authenticated"が返ってきます

スクリーンショット 2022-07-14 15.03.53.jpg

以下のファイルのauthorize_requestの処理が実行されていることが確認できます。

Tokneを付与していないのでerrorが処理が走っています。

app/controllers/secured_controller.rb
class SecuredController < ApplicationController
  before_action :authorize_request

  private

  def authorize_request
    authorize_request = AuthorizationService.new(request.headers)
    @current_user = authorize_request.current_user
  rescue JWT::VerificationError, JWT::DecodeError
    render json: { errors: ['Not Authenticated'] }, status: :unauthorized
  end
end

トークンを入れて新規作成(POST)を試す

次にトークンを入れて新規作成のメソットを実行してみます。

まずAuth0の管理画面を開きTokenを取得します。管理画面のTestタブの下記の部分よりTokenを取得します。

長いのでコピーする際は注意してください。

スクリーンショット 2022-07-12 17.09.43.jpg

次にpostman側で取得したTokenを格納していきます。

Authorizationのタブを選択肢TypeをBearer Tokenに設定します。

右側のTokneの中に先ほどAuth0から取得した値を貼り付けます。

スクリーンショット 2022-07-14 15.32.31.jpg

これで準備は完了なので先程と同様にparamに値をいれPOSTを叩いてみます。

スクリーンショット 2022-07-14 15.39.21.jpg

データが実際に登録されて、レスポンスが返ってきてることが確認できます。

取得(GET)メソットを試す

次にGETメソットを実行して、先ほど新規作成した記事が実際に登録されているかを確認します。

スクリーンショット 2022-07-14 15.41.55.jpg

Tokenを空にしてもGETメソットはレスポンスは返ってきます。

削除(DLETE)メソットを試す

idが2のデータの記事を削除する処理を行います。

こちらはTokneの値を入れないと認証エラーになります。

スクリーンショット 2022-07-14 15.56.09.jpg

GETメソットを実行するとidが2のデータが削除されていることが確認できます。

スクリーンショット 2022-07-14 15.56.59.jpg

以上でRails側のCRUDの確認が完了しました。

フロントとバックエンドの連携

最後にRailsとNext.jsの連携を進めていきます。

概要としては以下の内容を行います。

  • フロント側でログイン処理をする
  • ログイン後に付与されるTokneをフロント側のstateで管理する
  • Token情報を付与しフロント側からリクエストを送る

Rails側の設定

フロント側からのアクセスを許可するためにcorsの設定をおこないます。

config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:9000'

    resource '*',
            headers: :any,
            methods: %i[get post put patch delete options head]
  end
end

Next.js側の設定

環境変数の設定

先ほどRailsの.envに記載した下記の内容をNext.jsの環境変数にも書いて対応させます。

.env
AUTH0_IDENTIFIER=xxxxxxx

Next.js側の追記事項

NEXT_PUBLIC_BASE_URL='http://localhost:9000'
NEXT_PUBLIC_AUTH0_DOMAIN='xxxxxx'
NEXT_PUBLIC_AUTH0_CLIENT_ID='xxxxxx'
NEXT_APP_REST_URL="http://localhost:3000/api/v1"
NEXT_PUBLIC_AUTH0_AUDIENCE="xxxxx" // ここにAUTH0_IDENTIFIEの値を入れる

_appで環境変数を読み取る

pages/_app.tsx
import "../styles/globals.css";
import { Auth0Provider } from "@auth0/auth0-react";

function MyApp({ Component, pageProps }) {
  //ログイン後のリダイレクト先を指定
  const redirectUri = `${process.env["NEXT_PUBLIC_BASE_URL"]}/login`;
  return (
    <Auth0Provider
      domain={process.env["NEXT_PUBLIC_AUTH0_DOMAIN"]!}
      clientId={process.env["NEXT_PUBLIC_AUTH0_CLIENT_ID"]!}
      audience={process.env["NEXT_PUBLIC_AUTH0_AUDIENCE"]!} // 追記
      redirectUri={redirectUri}
    >
        <Component {...pageProps} />
    </Auth0Provider>
  );
}
export default MyApp;

これでNext.js側でログイン後に取得できるToKenを利用して、Rails側のアプリでも認証をすることができるようになりました。

aixosのインストール

API通信はaxiosを利用して行うのでインストールをする

npm install axios --save

Recoilの導入

今回はグローバルState(Recoil)でAuth0から取得できるTokenを管理します。

フロント側は以下の流れで処理を行います。

  • Auth0経由でログイン処理
  • ログイン後にリダイレクトするページでTokenを取得しRecoilへ格納
  • ブログ投稿ページへ遷移しRecoilからTokenを取得
  • 取得したTokenを使ってRails側に新規投稿のリクエストを送る

なおRecoilについての詳しい解説はここでは割愛するので、気になった方はこちらの記事を参考にしてみてください。

さっそくRecoilをインストールしていきます。

npm install recoil     

RecoilRootでアプリをラップする。

pages/_app.tsx
import "../styles/globals.css";
import { Auth0Provider } from "@auth0/auth0-react";
import { RecoilRoot } from "recoil";

function MyApp({ Component, pageProps }) {
  //ログイン後のリダイレクト先を指定
  const redirectUri = `${process.env["NEXT_PUBLIC_BASE_URL"]}/login`;
  return (
    <Auth0Provider
      domain={process.env["NEXT_PUBLIC_AUTH0_DOMAIN"]!}
      clientId={process.env["NEXT_PUBLIC_AUTH0_CLIENT_ID"]!}
      audience={process.env["NEXT_PUBLIC_AUTH0_AUDIENCE"]!}
      redirectUri={redirectUri}
    >
      <RecoilRoot>
        <Component {...pageProps} />
      </RecoilRoot>
    </Auth0Provider>
  );
}

export default MyApp;

次にRecoilで使うatomsの設定を行います。

recoil/atoms/tokenState.ts
import { atom } from "recoil";

const tokenState = atom({
  key: "tokenState",
  default: "",
});

export default tokenState;

ログイン後にトークンを取得しRecoilに格納する

Auth0側でログイン完了後にリダイレクトされるページでTokneを取得しRecoilに格納します。

pages/login.tsx
import { useAuth0 } from "@auth0/auth0-react";
import { NextPage } from "next";
import { useRouter } from "next/router";
import React, { useEffect } from "react";
// recoil
import { useSetRecoilState } from "recoil";
import tokenState from "../recoil/atoms/tokenState";

const LoginPage: NextPage = () => {
  const router = useRouter();
  const { isAuthenticated, logout, getAccessTokenSilently } = useAuth0();
  const setToken = useSetRecoilState(tokenState);

  // ログイン完了後にトークンを取得しRecoilへ格納
  useEffect(() => {
    const getToken = async () => {
      try {
        const accessToken = await getAccessTokenSilently({});
        setToken(accessToken);
      } catch (e) {
        console.log(e.message);
      }
    };
    getToken();
  }, []);

  return (
    <div>
      <h2>ログイン状態</h2>
      {isAuthenticated ? (
        <>
          <p>ログイン中です</p>
          <button onClick={() => logout({ returnTo: window.location.origin })}>
            ログアウト
          </button>
          <button
            onClick={() => {
              router.push("/blog");
            }}
          >
            記事投稿ページへ
          </button>
        </>
      ) : (
        <p>ログアウトしています</p>
      )}
    </div>
  );
};

export default LoginPage;
  • useAuth0()getAccessTokenSilentlyを利用してTokenを取得する
  • useSetRecoilState(tokenState)でRecoilのTokenを更新する
  • useEffect内でレンダリング時に処理が走るようにする
  • またuseRouter()でblogページへ遷移する導線を作成

ブログページでReocilからTokneを取得する

遷移したblogページでRecoilからTokneを取得できるか確認します。

pages/blog.tsx
import { NextPage } from "next";
import React from "react";
// recoil
import { useRecoilValue } from "recoil";
import tokenState from "../recoil/atoms/tokenState";

const BlogPage: NextPage = () => {
  const token = useRecoilValue(tokenState); // RecoilのTokneを取得する

  console.log(token);

  return <div>ブログ投稿</div>;
};

export default BlogPage;

ブラウザを確認するとconsoleでトークンが表示されていることが確認できるかと思います。

GETメソットでブログデータを取得する

まずはTokneを付与せずに実行できるGETメソットを使ってブログ記事が取得できるかを試してみます。

pages/blog.tsx
import axios from "axios";
import { NextPage } from "next";

import React, { useEffect } from "react";
// recoil
import { useRecoilValue } from "recoil";
import tokenState from "../recoil/atoms/tokenState";

const BlogPage: NextPage = () => {
  const token = useRecoilValue(tokenState);
  useEffect(() => {
    const getPosts = async () => {
      const res = await axios.get("http://localhost:3000/api/v1/posts");
      console.log(res);
    };
    getPosts();
  });

  return <div>ブログ投稿</div>;
};

export default BlogPage;

ブラウザで確認するとデータが取得できていることが確認できます。

スクリーンショット 2022-07-14 22.41.26.jpg

Tokneを付与してPOSTメソットを実行する

次はReocilのTokneをリクエストヘッダーに付与し記事を新規作成します。

まずはタイトルと説明を入力するフォームとstateを追加します。

確認ようなのでスタイルは特に当てていないです。

pages/blog.tsx
import { useAuth0 } from "@auth0/auth0-react";
import axios from "axios";
import { NextPage } from "next";

import React, { useEffect, useState } from "react";
// recoil
import { useRecoilValue } from "recoil";
import tokenState from "../recoil/atoms/tokenState";

const BlogPage: NextPage = () => {
  const token = useRecoilValue(tokenState);

  const [title, setTitle] = useState<string>("");
  const [caption, setCaption] = useState<string>("");

  const onChange = (
    e: React.ChangeEvent<HTMLInputElement>,
    setState: React.Dispatch<React.SetStateAction<string>>
  ) => {
    setState(e.target.value);
  };

  return (
    <div>
      <label htmlFor="">タイトル</label>
      <input
        type="text"
        value={title}
        onChange={(e) => {
          onChange(e, setTitle);
        }}
      />
      <br />
      <label htmlFor="">本文</label>
      <input
        type="text"
        value={caption}
        onChange={(e) => {
          onChange(e, setCaption);
        }}
      />
      <br />
      <button>新規投稿</button>
    </div>
  );
};

export default BlogPage;

次に入力されたデータをパラメタと送信するための新規登録用のAPIコールメソットを作成します。

pages/blog.tsx
import { useAuth0 } from "@auth0/auth0-react";
import axios from "axios";
import { NextPage } from "next";

import React, { useEffect, useState } from "react";
// recoil
import { useRecoilValue } from "recoil";
import tokenState from "../recoil/atoms/tokenState";

const BlogPage: NextPage = () => {
  const token = useRecoilValue(tokenState);

  const [title, setTitle] = useState<string>("");
  const [caption, setCaption] = useState<string>("");

  const onClick = () => {
    const params = {
      title: title,
      caption: caption,
    };
    console.log(token);

    axios
      .post("http://127.0.0.1:3000/api/v1/posts", params, {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      })
      .then((res) => {
        console.log(res);
      })
      .catch((error) => {
        console.log(error);
      });
  };

  const onChange = (
    e: React.ChangeEvent<HTMLInputElement>,
    setState: React.Dispatch<React.SetStateAction<string>>
  ) => {
    setState(e.target.value);
  };

  return (
    <div>
      <label htmlFor="">タイトル</label>
      <input
        type="text"
        value={title}
        onChange={(e) => {
          onChange(e, setTitle);
        }}
      />
      <br />
      <label htmlFor="">本文</label>
      <input
        type="text"
        value={caption}
        onChange={(e) => {
          onChange(e, setCaption);
        }}
      />
      <br />
      <button onClick={onClick}>新規投稿</button>
    </div>
  );
};

export default BlogPage;
  • axiosのheadersの中にRecoilに保存されていたTokenを入れる
  • paramsにtitleとcaptionの値を入れる
  • 登録後の成功を確認するためにresconsoleで表示させる

実際に値を入れて新規登録ボタンを押してみます

スクリーンショット 2022-07-15 9.27.50.jpg

consoleを確認すると成功のレスポンスが確認できます。

スクリーンショット 2022-07-15 9.29.35.jpg

Tokneをの値を空にして同様の処理を行うとNot Authenticatedのレスポンスが返ってきます。

最後に

いかがだったでしょうか。

今回はAuth0を利用してNext.js×RailsのSPA構成で認証機能の実装を行いました。

Auth0はドキュメントも豊富かつチュートリアルもあるのでサクッと導入することができるので、ぜひ今後の認証機能周りの開発の際に使っていただければと思います。

他にも記事を出しているので、併せて読んでいただけると嬉しいです。

105
91
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
105
91

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?