Elixir で OAuth2 を使って、 STRAVA のApiを叩いてみたい ①


この記事は、fukuoka.ex Elixir／Phoenix Advent Calendar 2019 の 9日目です。 昨日は @koyo-miyamura さんの「PhoenixでRedisを使った簡単ランキングの実装」でした。

この記事では、Elixir で OAuth2 の認証をやってみようと思います。

書いていたら、だんだんカオスになってきたので、今回は動作の準備という事で、tokenの取得までをゴールとします。

※ 12/10 続編書きました

[Elixir で OAuth2 を使って、 STRAVA のApiを叩いてみたい ②](https://qiita.com/tomoaki-kimura/items/92bd01145f9f0b47300d)

## 書いてる人

Elixir初心者のRails経験者のおっさん。

小倉のとあるもくもく会でご本人曰く、エンジニアもやるパン屋さん？に出会い、kokuraex に参加させて頂くようになりました。

## はじめに

脳筋エンジニアの皆さん、こんにちは。突然ですが、STRAVA使ってますか？

サイコンを忘れても、スマホさえあればアクティビティログが取れる便利グッズですので、運動を渇望するエンジニアにはもはや必須アイテムでしょう。

そして、そこにApiがあるのであれば、取得したくなるのが性というもの。

まずは、以下のサイトを参考に考えてみました。

### 参考サイト

- STRAVAのリファレンス(結構親切)
 - https://developers.strava.com/docs/getting-started/
- curlでアクセストークンを取得している先駆者様
 - http://hhhhhskw.hatenablog.com/entry/2018/11/06/014206
- phoenix 立ち上げまで
 - https://qiita.com/piacerex/items/b7787580fce5f148242f#_reference-15d862e0dedbe9d9069e
- OAuth2 を使ってGithubのソーシャルログインをされています。
 - https://qiita.com/mserizawa/items/32c6ff17a7cdd51b1975

### 環境

```
$ mix -v
Erlang/OTP 22 [erts-10.5.6] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Mix 1.9.4 (compiled with Erlang/OTP 22)
```

### 前提

phenixがインストールされている状況からスタートとなります。

もし、まだの方や、初めて触る方には、上でも紹介しましたが、

https://qiita.com/piacerex/items/6714e1440e3f25fb46a1

こちらのエントリーを最低②まで進めていただくとスムーズです。

## 1.アプリケーションの作成

任意のディレクトリから開始します。

```
$ mix phx.new oauth_test --no-webpack --no-ecto
```

phoenix で新しいアプリケーションを作るコマンドです。

`--no-webpack` は そのままなのですが、 webpack を使用しないモードです。

こちらは、なくても phoenix には node.js が組み込まれており、リアルタイムでレンダリングされる機能は使えます。

`--no-ecto` は DB を使用しないモードです。

今回は Api を叩く所が目的ですので、外しておきます。
外さないと、DB由来のエラーが出るので気を付けて下さい。

では、上記の実行結果ですが、以下のようになります。

```
.
（略）
.
.
* creating oauth_test/priv/static/images/phoenix.png
* creating oauth_test/priv/static/favicon.ico

Fetch and install dependencies? [Yn] y
* running mix deps.get
* running mix deps.compile

We are almost there! The following steps are missing:

    $ cd oauth_test

Start your Phoenix app with:

    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server

$ 
```

出来ました。

ログにアプリのスタート方法が出ていますね。

```
$ cd oauth_test
```

でプロジェクトに移動して、

```
$ mix phx.server
```

でサーバースタートです。

※（一番下に書いてある、 `$ iex -S mix phx.server` は iex を phoenix のライブラリを読み込んだ状態で起動しつつ、サーバー起動に移行出来るモードです。後で使います。）

起動すると、以下のようなログが出てきます。

```
09:13:Desktop$ cd oauth_test/
09:13:oauth_test$ mix phx.server
[info] Running OauthTestWeb.Endpoint with cowboy 2.7.0 at 0.0.0.0:4000 (http)
[info] Access OauthTestWeb.Endpoint at http://localhost:4000
```

ポート番号は `4000` 番ですね。

ですので、　ブラウザに `localhost:4000` と打てばトップページが見られます。

![](https://gyazo.com/3071f0972c6c16b9d16488f8d078a47f.png)

サーバーを止めるには、 `ctrl` + `c` を２回押します。

一旦止めましょう。

```
^C
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution
^$ 
```

入力画面に戻ればOKです。

## 2.OAuth2 のインストール

次に、ライブラリをインストールします。

認証用のライブラリ、OAuth2 を入れていきましょう。

### 2.1 mix.exs

プロジェクト直下にある、`mix.exs` というのがどうやら、ライブラリの管理をしているようです。

![](https://gyazo.com/36495ca72775cf8af143d33ba8dc0827.png)


/mix.exs

```
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {OauthTest.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.4.11"},
      {:phoenix_pubsub, "~> 1.1"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.0"}
    ]
  end
```

ここにライブラリの情報を入れます。

/mix.exs

```
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {OauthTest.Application, []},
      extra_applications: [:logger, :runtime_tools, :oauth2]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.4.11"},
      {:phoenix_pubsub, "~> 1.1"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.0"},
      {:oauth2, "~> 2.0"}
    ]
  end
```

`extra_applications: [:logger, :runtime_tools, :oauth2]`

`{:oauth2, "~> 2.0"}`

この２行が追加箇所となります。

### 2.2 インストール

ライブラリのインストールには、 `deps.get` を使います。

```
$ mix deps.get
Resolving Hex dependencies...
Dependency resolution completed:
Unchanged:
  cowboy 2.7.0
  cowlib 2.8.0
  file_system 0.2.7
  gettext 0.17.1
  jason 1.1.2
  mime 1.3.1
  phoenix 1.4.11
  phoenix_html 2.13.3
  phoenix_live_reload 1.2.1
  phoenix_pubsub 1.1.2
  plug 1.8.3
  plug_cowboy 2.1.0
  plug_crypto 1.0.0
  ranch 1.7.1
  telemetry 0.4.1
New:
  certifi 2.5.1
  hackney 1.15.2
  idna 6.0.0
  metrics 1.0.1
  mimerl 1.2.0
  oauth2 2.0.0
  parse_trans 3.3.0
  ssl_verify_fun 1.1.5
  unicode_util_compat 0.4.1
* Getting oauth2 (Hex package)
* Getting hackney (Hex package)
* Getting certifi (Hex package)
* Getting idna (Hex package)
* Getting metrics (Hex package)
* Getting mimerl (Hex package)
* Getting ssl_verify_fun (Hex package)
* Getting unicode_util_compat (Hex package)
* Getting parse_trans (Hex package)
```

フレームワークではお馴染みの方式ですが、予め書いてあるmix.exsの内容を読んで自動でHexからインストールしてくれます。

New:　の箇所が新たに追加されたライブラリです。

## 3.STRAVA の設定

### 3.1 STRAVA API

詳しくは、

https://developers.strava.com/docs/getting-started/

こちらのリファレンスを見ていただくと良いですが、最低限必要なものは以下の通りです。

下記の画像では、

- クライアントID
- クライアントシークレット（なぜかシートになってますが・・）

この２点の値を後ほど使います。

![](https://gyazo.com/2e48b50e49fa812739a90d3a938d8067.png)


さらに画面の下に行き、下記の部分、

- 認証コールバックドメイン
- ウェブサイト

こちらの２点の設定が必要です。

`認証コールバックドメイン` は、ローカルでの開発環境においては、 `localhost` としておいて下さい。（ポート番号は不要ですし、入れられません）

また、設定を行う為には、

`ウェブサイト` に何らかの URL を入力しますが、これは、 `http(s)://` と、`.com` など(comじゃなくても可)の文字があれば存在しない URLでも何でも良いようです。

![](https://gyazo.com/dd502b0f0d4ca7dd9e2face535ac90ca.png)

### 3.2 iex での検証

では、一旦ここで、メモでも何でも良いので、文字化けしない環境で以下の設定を書いておきましょう。

```
client = OAuth2.Client.new([
  strategy: OAuth2.Strategy.AuthCode,
  client_id: "クライアントID",
  client_secret: "クライアントシークレット",
    site: "https://www.strava.com",
    redirect_uri: "http://localhost:4000/auth",
    authorize_url: "https://www.strava.com/oauth/authorize",
    token_url: "https://www.strava.com/oauth/token",
    token_method: :post,
    params: %{"grant_type": "authorization_code"}
  ])
uri = OAuth2.Client.authorize_url!(client, scope: "activity:read_all")
uri = URI.decode(uri)
```

`クライアントID` `クライアントシークレット` は、STRAVAの設定ペーじより、ご自身の設定を書き入れて下さい。

準備が出来たら、以下のコマンドで、iex を起動します。

```
$ iex -S mix phx.server
```

起動したら、上でメモった設定と変数をコピペして下さい。

```
Erlang/OTP 22 [erts-10.5.6] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

[info] Running OauthTestWeb.Endpoint with cowboy 2.7.0 at 0.0.0.0:4000 (http)
[info] Access OauthTestWeb.Endpoint at http://localhost:4000
Interactive Elixir (1.9.4) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)>     client = OAuth2.Client.new([
...(1)>       strategy: OAuth2.Strategy.AuthCode,
...(1)>       client_id: "xxxxxx",
...(1)>       client_secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
...(1)>       site: "https://www.strava.com",
...(1)>       redirect_uri: "http://localhost:4000/auth",
...(1)>       authorize_url: "https://www.strava.com/oauth/authorize",
...(1)>       token_url: "https://www.strava.com/oauth/token",
...(1)>       token_method: :post,
...(1)>       params: %{grant_type: "authorization_code"}
...(1)>     ])
%OAuth2.Client{
  authorize_url: "https://www.strava.com/oauth/authorize",
  client_id: "xxxxxx",
  client_secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  headers: [],
  params: %{grant_type: "authorization_code"},
  redirect_uri: "http://localhost:4000/auth",
  ref: nil,
  request_opts: [],
  serializers: %{},
  site: "https://www.strava.com",
  strategy: OAuth2.Strategy.AuthCode,
  token: nil,
  token_method: :post,
  token_url: "https://www.strava.com/oauth/token"
}


iex(2)>uri = OAuth2.Client.authorize_url!(client, scope: "activity:read_all")
"https://www.strava.com/oauth/authorize?approval_prompt=auto&client_id=xxxxx&redirect_uri=http%3A%2F%2Flocalhost%3A4000%2Fauth&response_type=code&scope=activity%3Aread_all"

iex(3)>uri = URI.decode(uri)
"https://www.strava.com/oauth/authorize?approval_prompt=auto&client_id=xxxxx&redirect_uri=http://localhost:4000/auth&response_type=code&scope=activity:read_all"
```

こんな感じでコピペして下さい。

最後の、URL

"https://www.strava.com/oauth/authorize?approval_prompt=auto&client_id=xxxxx&redirect_uri=http://localhost:4000/auth&response_type=code&scope=activity:read_all"

このURLにアクセス出来る事を確認して下さい。
(client_id=xxxxx の部分はご自身のクライアントIDが入っているはずです)

![](https://gyazo.com/940e68af0d599c0cd1a1a5d58730e188.png)

この画面が出てきたら、準備OKです。

ちなみにですが、 `activity:read_all` の部分は、見る事が出来る権限の設定となっています。今回は、アクティビティを読み込む事の出来る設定となっています。

https://developers.strava.com/docs/oauth-updates/

## 4.ルーティング

### 

今回使うページを設定しておきましょう。

今回使うのは、とりあえずで2ページ

ルーティング用のファイルは、 `lib/oauth_test/` 直下にあります。

![](https://gyazo.com/0fdb37c1f009ac696df7db5eee89a01d.png)

/lib/oauth_test/router.ex

```
  scope "/", OauthTestWeb do
    pipe_through :browser

    get "/", PageController, :index
  end
```

この部分を

```
  scope "/", OauthTestWeb do
    pipe_through :browser

    get "/", PageController, :index
    get "/auth", PageController, :auth
  end
```

このように書き足して下さい。

これで、初期ページに加えて、１ページ追加されました。

確認は `mix phx.routes` で行います。

```
$ mix phx.routes
Compiling 2 files (.ex) 
page_path  GET  /                  OauthTestWeb.PageController :index
page_path  GET  /auth              OauthTestWeb.PageController :auth
websocket  WS   /socket/websocket  OauthTestWeb.UserSocket
```

## 5.コントローラー

ここからは、下の３つの画像の流れで、トークン取得に必要な認証コードを取得する所までやっていきます。

1) トップページに認証用のリンクを貼る

![](https://gyazo.com/1753ca2261453e7b151145d9b2b9bc2c.png)

2) アプリの連携を許可する

![](https://gyazo.com/940e68af0d599c0cd1a1a5d58730e188.png)

3)認証コードの取得とパラメーターの抜き出し。

![](https://gyazo.com/9d928090d5b65ddc8aed301a4b5148ec.png)

では、実装に入りましょう。

コントローラーは `lib/auth_test_web/controllers/` 以下になります。

![](https://gyazo.com/fe9a7ebbc1069bc32417637f3608e495.png)


今回はデフォルトで生成されていたコントローラーを使います。

/lib/oauth_test_web/contorollers/page_controller.ex

```
defmodule OauthTestWeb.PageController do
  use OauthTestWeb, :controller

  def index(conn, _params) do
    render(conn, "index.html")
  end
end
```

この記述を以下のように編集して下さい。

```
defmodule OauthTestWeb.PageController do
  use OauthTestWeb, :controller

  def index(conn, _params) do
    client = OAuth2.Client.new([
      strategy: OAuth2.Strategy.AuthCode,
      client_id: "xxxxxxxx",
      client_secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      site: "https://www.strava.com",
      redirect_uri: "http://localhost:4000/auth",
      authorize_url: "https://www.strava.com/oauth/authorize",
      token_url: "https://www.strava.com/oauth/token",
      token_method: :post,
      params: %{grant_type: "authorization_code"}
    ])

    uri = OAuth2.Client.authorize_url!(client, scope: "activity:read_all")
    uri = URI.decode(uri)
    render(conn, "index.html", uri: uri)
  end

  def auth(conn, %{"code" => code}) do
    render(conn, "auth.html", code: code)
  end
end
```

indexアクションの記述ですが、こちらは先程 iex で実験をした時のメモから作っています。

authアクションでは、コールバックで戻ってきたトークン、3)画像の赤枠の部分 の値を テンプレート側で、 `@code` として使えるようにパラメーターを 変数にセットしています。

`%{"code" => code}` でパラメーターの値をアクション側の変数にセットし、

`code: code` で テンプレート側に `@code` として引き渡せるようでした。

## 6.テンプレート

### 6.1 index.html.eex

テンプレートは、`lib/oauth_test_web/templates/` 以下です。

![](https://gyazo.com/19c5a4fb359946cc43fc6c1ae72bcf46.png)


/lib/oauth_test_web/templates/page/index.html.eex

```
section class="phx-hero">
  <h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
  <p>A productive web framework that<br/>does not compromise speed or maintainability.</p>
</section>

<section class="row">
  <article class="column">
    <h2>Resources</h2>
    <ul>
      <li>
        <a href="https://hexdocs.pm/phoenix/overview.html">Guides &amp; Docs</a>
      </li>
      <li>
        <a href="https://github.com/phoenixframework/phoenix">Source</a>
      </li>
      <li>
        <a href="https://github.com/phoenixframework/phoenix/blob/v1.4/CHANGELOG.md">v1.4 Changelog</a>
      </li>
    </ul>
  </article>
  <article class="column">
    <h2>Help</h2>
    <ul>
      <li>
        <a href="https://elixirforum.com/c/phoenix-forum">Forum</a>
      </li>
      <li>
        <a href="https://webchat.freenode.net/?channels=elixir-lang">#elixir-lang on Freenode IRC</a>
      </li>
      <li>
        <a href="https://twitter.com/elixirphoenix">Twitter @elixirphoenix</a>
      </li>
    </ul>
  </article>
</section>
```

現状では、初期画面の表示となっていますので、全て消して以下のように `書き換えて` 下さい。

```
<%= link "データーを取得する", to: @uri %>
```

リンク１行だけです。

このリンクはコントローラー側で作った外部リンクとなります。

### 6.2 auth.html.eex

auth は新しいルートですので、ファイルを新規作成する必要があります。

```
$ touch lib/oauth_test_web/templates/page/auth.html.eex 
```

![](https://gyazo.com/e5a6d8c72cd5bd9a7c507ab7f7b981d0.png)

中身は、

/lib/oauth_test_web/templates/page/auth.html.eex

```
<%= @code %>
```

また、このように１行だけ実装します。

これで、一旦起動しましょう。

```
$ mix phx.server
```

サーバーを立ち上げ、


![](https://gyazo.com/1753ca2261453e7b151145d9b2b9bc2c.png)


![](https://gyazo.com/940e68af0d599c0cd1a1a5d58730e188.png)


![](https://gyazo.com/9d928090d5b65ddc8aed301a4b5148ec.png)

この通りに辿る事ができれば、一旦OKとします。

## 7.トークンの取得

### 7.1 コントローラーの変更

いよいよトークンの取得です。

/lib/oauth_test_web/contorollers/page_controller.ex

```
defmodule OauthTestWeb.PageController do
  use OauthTestWeb, :controller

  def index(conn, _params) do
    client = OAuth2.Client.new([
      strategy: OAuth2.Strategy.AuthCode,
      client_id: "xxxxx",
      client_secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      site: "https://www.strava.com",
      redirect_uri: "http://localhost:4000/auth",
      authorize_url: "https://www.strava.com/oauth/authorize",
      token_url: "https://www.strava.com/oauth/token",
      token_method: :post,
      params: %{grant_type: "authorization_code"}
    ])
    uri = OAuth2.Client.authorize_url!(client, scope: "activity:read_all")
    render(conn, "index.html", uri: uri)
  end

  def auth(conn, %{"code" => code}) do
    client = OAuth2.Client.new([
      strategy: OAuth2.Strategy.AuthCode,
      client_id: "xxxxx",
      client_secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      site: "https://www.strava.com",
      redirect_uri: "http://localhost:4000/auth",
      authorize_url: "https://www.strava.com/oauth/authorize",
      token_url: "https://www.strava.com/oauth/token",
      token_method: :post,
      params: %{grant_type: "authorization_code"}
    ])
    client = OAuth2.Client.get_token!(client, code: code, client_id: client.client_id, client_secret: client.client_secret)
    access_token = client.token.access_token
    render(conn, "auth.html", access_token: access_token)
  end
end
```

### 7.2 テンプレートの変更

引き続きテンプレート側です。

/lib/oauth_test_web/templates/page/auth.html.eex

```
<%= inspect Poison.decode(@access_token) %>
```

この時点で `Poison` というライブラリを入れています。

取れる値がどうやらJSONのようでしたので、デコード出来るようにしておきます。

ですので、起動前に、mix.exsを、

/mix.exs


```
  defp deps do
    [
      {:phoenix, "~> 1.4.11"},
      {:phoenix_pubsub, "~> 1.1"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.0"},
      {:oauth2, "~> 2.0"},
      {:poison, "~> 4.0"}
    ]
  end
```

 このように変更後、

```
 $ mix deps.get
```

でインストールしておいて下さい。

では、起動します。

例のごとくボタンを辿ると、最後の画面が、

![](https://gyazo.com/d7a348815b1d1ff2747386185ddab06e.png)

このように変更されて、 access_token が取得出来ていればOKです。

## 次回予告

さすがにちょっと・・・という事で、コントローラー周りのリファクタリングと、アクティビティデーターから、地図の表示までをやってみたいと思います。

正直、私のElixirレベルが上がらないと、厳しいです。せっかく良い環境にいるので、勉强しなくてはですね。

[Elixir で OAuth2 を使って、 STRAVA のApiを叩いてみたい ②](https://qiita.com/tomoaki-kimura/items/92bd01145f9f0b47300d)