Help us understand the problem. What is going on with this article?

Materiaを導入してログイン認証してみる

Materiaについては、
「Webサービスバックエンドライブラリー Materia」
を参照ください。

前提環境

> mix hex.info
Hex:    0.18.2
Elixir: 1.6.1
OTP:    20.2.4

> mix phx.new -v
Phoenix v1.3.4

プロジェクトの作成とMateriaの導入

プロジェクトを作成する

> mix phx.new materia_sample --no-brunch
> cd materia_sample
materia_sample/mix.exs
defp deps do
    [
      {:phoenix, "~> 1.3.2"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_ecto, "~> 3.2"},
      {:plug_cowboy, "~> 1.0"},  #<- add here
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.10"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:cowboy, "~> 1.0"},
      {:materia, "~> 0.1.0"}, #<- add here
    ]
  end

guardian(認証ライブラリ)の設定

※必ずdeps.getの前に実施する

materia_sample/config/config.exs
# Configures Guardian
config :materia, Materia.Authenticator,
  issuer: "materia_sample",
  access_token_ttl: {10, :minutes}, #必須
  refresh_token_ttl: {1, :days}, # refresh_tokenを定義しない場合sign-inはaccess_tokenのみ返す
  user_registration_token_ttl: {35, :minutes},
  password_reset_token_ttl: {35, :minutes},
  secret_key: "your secusecret token",
  allowed_algos: ["HS256"]

# Configures GuardianDB
config :guardian, Guardian.DB,
repo: MateriaSample.Repo,  #<- mod your app repo
schema_name: "guardian_tokens", # default
#token_types: ["refresh_token"], # store all token types if not set
sweep_interval: 60 # default: 60 minutes

# Configures calender utils locale (default is server locale)
config :materia_utils, calender_locale: "Asia/Tokyo"
materia_sample/config/dev.exs
# Configure materia repo
config :materia, :repo, MateriaSample.Repo  #<- add your app repo

deps.get

mix deps.get

シークレットキーの生成

> mix phx.gen.secret
hogehogehogehogheohgoehgoheohgeohgoheohgoehgoheohgeohgoehogeho

config.exsの「Configures Guardian」の末尾にシークレットキーを追加

materia_sample/config/config.exs
# Configures Guardian
config :materia, Materia.Authenticator,
  issuer: "materia_sample",
  
  allowed_algos: ["HS256"],   # カンマを追加
  # Generate mix task
  # > mix phx.gen.secret
  secret_key: "hogehogehogehogheohgoehgoheohgeohgoheohgoehgoheohgeohgoehogeho"

※オプション guardian sweeper(tokenの自動削除)の有効化

materia_sample/lib/materia_sample/application.ex
defmodule MateriaSample.Application do
  use Application

〜中略〜

    # Define workers and child supervisors to be supervised
    children = [
      # Start the Ecto repository
      supervisor(MateriaSample.Repo, []),
      # Start the endpoint when the application starts
      supervisor(MateriaSampleWeb.Endpoint, []),
      # Start your own worker by calling: MateriaSample.Worker.start_link(arg1, arg2, arg3)
      # worker(MateriaSample.Worker, [arg1, arg2, arg3]),
      worker(Guardian.DB.Token.SweeperServer, []), #<- if you wont auto sweep invalid token, you must add GuardianDB worker.
    ]

〜中略〜

end

seedsの設定

Materiaのテスト用seeds.exsから転記


Migrationファイルの生成&DBマイグレーション

> mix guardian.db.gen.migration
> mix materia.gen.migration
> mix ecto.create
> mix ecto.migrate
> mix run priv/repo/seeds.exs

基本的な導入作業はここまでです。

基本的なAPIを有効にして使ってみる

Materiaで用意されたルーティングを追加して起動

※ 実際のルーティング定義はサービス要件に合わせて設定する必要があります。
 具体的にはユーザー情報の検索などは管理者権限の運用者のみ利用できるように設定すべきです。

materia_sample/lib/materia_sample_web/router.ex
defmodule MateriaWeb.Router do
  use MateriaWeb, :router

  # 今回は使わないのでコメントアウト
  #pipeline :browser do
  #  plug :accepts, ["html"]
  #  plug :fetch_session
  #  plug :fetch_flash
  #  plug :protect_from_forgery
  #  plug :put_secure_browser_headers
  #end

  pipeline :guardian_auth do
    plug Materia.AuthenticatePipeline
  end

  pipeline :tmp_user_auth do
    plug Materia.UserRegistrationAuthPipeline
  end

  pipeline :pw_reset_auth do
    plug Materia.PasswordResetAuthPipeline
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  pipeline :guardian_auth do
    plug Materia.AuthenticatePipeline
  end

  pipeline :tmp_user_auth do
    plug Materia.UserRegistrationAuthPipeline
  end

  pipeline :pw_reset_auth do
    plug Materia.PasswordResetAuthPipeline
  end

  scope "/api", MateriaWeb do
    pipe_through :api

    post "/sign-in", AuthenticatorController, :sign_in
    post "/refresh", AuthenticatorController, :refresh
    post "/tmp-registration", UserController, :registration_tmp_user
    post "/request-password-reset", UserController, :request_password_reset

  end

  scope "/api", MateriaWeb do
    pipe_through [ :api, :tmp_user_auth]

    get "/varidation-tmp-user", AuthenticatorController, :is_varid_token
    post "/user-registration", UserController, :registration_user
    post "/user-registration-and-sign-in", UserController, :registration_user_and_sign_in

  end

  scope "/api", MateriaWeb do
    pipe_through [ :api, :pw_reset_auth]

    get "/varidation-pw-reset", AuthenticatorController, :is_varid_token
    post "/reset-my-password", UserController, :reset_my_password

  end

  scope "/api", MateriaWeb do
    pipe_through [ :api, :guardian_auth]

    get "/user", UserController, :show_me
    post "/grant", GrantController, :get_by_role
    post "/sign-out", AuthenticatorController, :sign_out
    get "/auth-check", AuthenticatorController, :is_authenticated
    post "/search-users", UserController, :list_users_by_params
    resources "/addresses", AddressController, except: [:new, :edit]
    post "/create-my-addres", AddressController, :create_my_address

  end

〜中略〜

  #今回はいらないのでコメントアウト
  #scope "/", MateriaSampleWeb do
  #  pipe_through :browser # Use the default browser stack
  #
  #  get "/", PageController, :index
  #end

end

phoenixを起動する

> mix phx.server

※ ここからは自分の好きなHTTPクライアントを使用してください。
サンプルはREST Clientで記載しています。
VS Code上でHTTPリクエストを送信し、VS Code上でレスポンスを確認できる「REST Client」拡張の紹介

ログインする

  • Request
POST http://localhost:4000/api/sign-in HTTP/1.1
Content-Type: application/json

{
    "email": "hogehoge@example.com", 
    "password": "hogehoge"
  }
  • Response
HTTP/1.1 201 Created
server: Cowboy
date: Fri, 14 Dec 2018 08:16:32 GMT
content-length: 740
content-type: application/json; charset=utf-8
cache-control: max-age=0, private, must-revalidate

{
  "refresh_token": "hogehoge",
  "id": 1,
  "access_token": "fugafuga"
}


ユーザー情報を確認する

  • Request
GET http://localhost:4000/api/user HTTP/1.1
Content-Type: application/json
Authorization: Bearer fugafuga
  • Response
HTTP/1.1 200 OK
server: Cowboy
date: Fri, 14 Dec 2018 08:21:53 GMT
content-length: 941
content-type: application/json; charset=utf-8
cache-control: max-age=0, private, must-revalidate

{
  "status": 1,
  "role": "admin",
  "phone_number": null,
  "organization": {
    "users": [],
    "status": 1,
    "profile_img_url": "https://hogehoge.com/prof_img.jpg",
    "phone_number": null,
    "one_line_message": "let's do this.",
    "name": "hogehoge.inc",
    "lock_version": 1,
    "id": 1,
    "hp_url": "https://hogehoge.inc",
    "back_ground_img_url": "https://hogehoge.com/ib_img.jpg",
    "addresses": []
  },
  "name": "hogehoge",
  "lock_version": 2,
  "id": 1,
  "icon_img_url": null,
  "external_user_id": null,
  "email": "hogehoge@example.com",
  "descriptions": null,
  "back_ground_img_url": null,
  "addresses": [{
    "zip_code": "810-ZZZZ",
    "user": [],
    "subject": "billing",
    "organization": [],
    "longitude": null,
    "lock_version": 0,
    "location": "福岡県",
    "latitude": null,
    "id": 2,
    "address2": "大名 x-x-xx",
    "address1": "福岡市中央区"
  }, {
    "zip_code": "810-ZZZZ",
    "user": [],
    "subject": "living",
    "organization": [],
    "longitude": null,
    "lock_version": 0,
    "location": "福岡県",
    "latitude": null,
    "id": 1,
    "address2": "港 x-x-xx",
    "address1": "福岡市中央区"
  }]
}

他のユーザーに対する汎用検索を実施

  • Request
### ユーザー汎用検索
POST http://localhost:4000/api/search-users HTTP/1.1
Content-Type: application/json
Authorization: Bearer fugafuga

 {
    "and": [{"role": "admin"}],
    "or": [{"status": 8}, {"status": 1}]
  }
  • Response
[{
  "status": 1,
  "role": "admin",
  "phone_number": null,
  "organization": {
    "users": [],
    "status": 1,
    "profile_img_url": "https://hogehoge.com/prof_img.jpg",
    "phone_number": null,
    "one_line_message": "let's do this.",
    "name": "hogehoge.inc",
    "lock_version": 1,
    "id": 1,
    "hp_url": "https://hogehoge.inc",
    "back_ground_img_url": "https://hogehoge.com/ib_img.jpg",
    "addresses": []
  },
  "name": "hogehoge",
  "lock_version": 2,
  "id": 1,
  "icon_img_url": null,
  "external_user_id": null,
  "email": "hogehoge@example.com",
  "descriptions": null,
  "back_ground_img_url": null,
  "addresses": []
}]

ログアウトする

  • Request
POST http://localhost:4000/api/sign-out HTTP/1.1
Content-Type: application/json
Authorization: Bearer fugafuga
  • Response
HTTP/1.1 200 OK
server: Cowboy
date: Fri, 14 Dec 2018 08:41:12 GMT
content-length: 11
content-type: application/json; charset=utf-8
cache-control: max-age=0, private, must-revalidate

{
  "ok": true
}

再度ログインせずに実行すると認証エラー

  • Request
### ユーザー汎用検索
POST http://localhost:4000/api/search-users HTTP/1.1
Content-Type: application/json
Authorization: Bearer fugafuga

 {
    "and": [{"role": "admin"}],
    "or": [{"status": 8}, {"status": 1}]
  }
  • Response
HTTP/1.1 401 Unauthorized
server: Cowboy
date: Fri, 14 Dec 2018 08:42:26 GMT
content-length: 27
cache-control: max-age=0, private, must-revalidate

{"message":"invalid_token"}
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away