【Next.js】Auth0で認証したユーザのデータを取得してみた
この記事ではAuth0でログインしたユーザに紐付いたデータを取得してみます。
下記の記事で紹介している自作サイトでかなり苦労した所でもあります。
他にもっといい方法があったらコメントで教えてください!
環境の構築やフロント側のAuth0との連携は割愛させていただきます。
必要な方はこちらを参考にしてみてください!
使用技術
Next.js 動的ルーティングが便利なため
Axios
Ruby on Rails
MySql
Auth0 ログイン機能
どうやって実現するのか
フロントの動き
まずフロント側でAuth0に認証します。認証をするとユーザID
やAccessToken
を保持できます。
ユーザのデータ取得時に保持しているAccessToken
をバックエンドに送ります。
バックエンドの動き
バックエンドでは送られて来たAccessToken
を使ってAuth0に認証済みのAccessTokenなのか確認します。認証済みならユーザID
に紐付いたデータをDBから取得してフロントに送ります。
Railsの設定
フロントと通信できるように設定を行っていきます。
CORS設定
RailsにCORSの設定を行いましょう。
まずはGemfile
のrack-cors
のコメントを外しましょう。
gem 'rack-cors' # コメントを外す
コメントを外せたらBundle install
しておきましょう。
docker-compose build
docker-compose run --rm api bundle install
続いてrack-cors
の設定ファイルを編集します。
origins
にはfront
側のURLを指定します。
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'localhost:8000'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head], :expose => ['access-token']
end
end
ここではmethods
に指定している、expose
が大事になります。
expose
はバックエンドがheader
で受取たいものを指定します。
Modelの作成
テスト用のデータを作成して行きましょう。
今回はList
テーブルにtitle
とbody
と特定のユーザのデータを取得するためにuser_id
カラムを作成します。
docker-compose run api bundle exec rails g model List title:string body:text userId:string
docker-compose run api bundle exec rails db:migrate
テーブルの作成ができたので、データを入れていきます。
まずはseeds.rb
に入れるデータを書きます。
今回はテストも兼ねて取得しないデータも記載します。
5.times do |number|
List.create(title: "取得しないでね#{number}", body: "取得してないよね#{number}", user_id: "01")
List.create(title: "取得すべき#{number}", body: "取得できてる#{number}", user_id: "autn0のユーザID")
end
Auth0のユーザIDはここで確認できます。
Auth0
にログインしてusers
ページにいきます。
ユーザIDと書いてある所にauth0|
から始まるIDを入力します。
できたらデータを入れましょう。
docker-compose run api bundle exec rails db:seed
Controllerの作成
続いてコントロールを作成しましょう。
routes.rb'に追加します。 '''ruby:api/config/routes.rb Rails.application.routes.draw do namespace :api do resources :list, only: :index end end '''
api/app/conrtollersディレクトリ内に
api`ディレクトリを作成します。
cd api/app/controllers/
mkdir api
作成したapi
ディレクトリ内にList_controller.rb
を作成します。
cd api
touch List_controller.rb
編集しましょう。
class Api::ListController < ApplicationController
def index
@List = List.all
render json: @List
end
end
ここまでできたらデータが取得できているか確認してみましょう!
データが表示されたら成功です!
http://localhost:3000/api/list
Next.js非同期通信処理
次はフロント側の実装です。
フロントからAPIにuserId
とAccessToken
を送ります。
※Auth0との認証方法は割愛します。
axios導入
axios
を使ってAPI通信するので、インストールします。
docker-compose run front npm --prefix ./ディレクトリ名 install axios
データ取得
データ取得処理を実装していきます。
まず環境変数を指定するためenv
ファイルを作成します。
touch .env.local
.env.local
ファイルにAPIのURLを指定して起きます。
NEXT_PUBLIC_API_URL="http://localhost:3000"
pages
ディレクトリ内にlist.tsx
ファイルを作成します。
cd pages
touch list.tsx
list.tsx
を編集します。
import React, {useEffect, useState} from "react";
import { useAuth0 } from "@auth0/auth0-react";
import axios from "axios";
interface Contents {
title: string,
bodu: string
}
const List: React.FC= () => {
const {getAccessTokenSilently, isAuthenticated} = useAuth0();
const [list, setList] = useState<Contents[] | undefined>(undefined);
const getList = async() => {
const token = await getAccessTokenSilently();
const url = process.env.NEXT_PUBLIC_API_URL;
const res = await axios.get<Contents[]>(url +'/api/list/', { headers: {
Authorization: "Bearer " + token
},
})
setList(res.data)
}
useEffect(() => {
getList()
}, [])
return(
<div>
{list === undefined ? 'データが存在しません' :
<ul>
{list.map((item, index) => (
<li key={index}>{item.title}</li>
))}
</ul>
}
</div>
);
}
export default List;
上記のコードで大事な部分を抜粋しました。
const token = await getAccessTokenSilently();
const res = await axios.get<Contents[]>(url +'/api/list/', {
headers: {
Authorization: "Bearer " + token
},
})
ログイン認証して証明のTokenを取得して、これを送っています。
こうすれば誰のデータが必要かAPIに知らせることができます。
Rails認証設定
次はフロントから送られてきたトークンをAuth0に問い合わせをして、ログイン済みユーザなのか確認します。
JWT追加
JWT
を追加します。
gem 'JWT'
docker-compose build
docker-compose run --rm api bundle install
認証処理追加
こちらを元に実装していきます。
https://auth0.com/docs/quickstart/backend/rails/01-authorization
lib/json_web_token.rb
を作成して編集します。
cd lib
touch 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://YOUR_DOMAIN/',
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://YOUR_DOMAIN/.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
json_web_token.rb
が使用できるようにconfig/initializers
ディレクトリ内にjwt.rb
を作成して編集します。
require './lib/json_web_token'
次にapp/controllers/concerns/secured.rb
を作成して編集します。
# app/controllers/concerns/secured.rb
module Secured
extend ActiveSupport::Concern
included do
before_action :authenticate_request!
end
private
def authenticate_request!
@auth_user_id = auth_token[0]['sub']
rescue JWT::VerificationError, JWT::DecodeError
render json: { errors: ['Not Authenticated'] }, status: :unauthorized
end
def http_token
if request.headers['Authorization'].present?
request.headers['Authorization'].split(' ').last
end
end
def auth_token
JsonWebToken.verify(http_token)
end
end
上記コードは認証完了後、@auth_user_id
にユーザーIDを入れるようにしてあります。
最後にList_controller.rb
に認証処理が動く用に編集します。
class Api::ListController < ApplicationController
include Secured
def index
@List = List.where(userId: @auth_user_id)
render json: @List
end
end
動作確認
ここまできたら確認してみましょう!
docker-compose up -d
ログイン後http://localhost:8000/listにアクセスすると
ユーザーのデータだけが無事に表示されました!
他にもっといい方法があれば教えていただけると幸いです!
終わり
ここまで見てくださりありがとうございました!
ツイッターもやっているのでぜひフォローもお願いします!
Twitter
Discussion