前回の続きですが, 一応rails側からfacebook apiのデータ取得できたのでメモを残しておきます.

dai7igarashi.hatenablog.com

 

 

rails側のコード

いまいち慣れてないのでこれが正解かはわかりませんが...

require 'uri'
require 'net/https'
require 'json'

auth = request.env['omniauth.auth']
access_token = auth.credentials.token

uri = URI.parse("https://graph.facebook.com/v3.1/me?fields=posts{link,picture,message}")

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
resp = http.start {
http.get("#{uri.request_uri}&access_token=#{access_token}")
}
facebook_datas = JSON.parse(resp.body)

で一応, oauth認証したユーザの投稿{リンク, 画像, 文章} が取れます. 取り出すときは, facebook_datas["posts"]["data"]で取れます. (facebook_datas.posts.datasだと取れない...)

 

これから

やはりgraph apiの指定の仕方がわからないので, グラフAPIエクスプローラと格闘します.

Docker+Rails+webpacker ~facebook api を叩くためのssl設定~

twitter apiに引き続き今度はfacebookのgraph apiを叩きます. (まだ取得まで出来てませんがここまでをメモしておきます).

 

※因みに twitter apirails側からではなくブラウザ側(Reactでfetch)したらcorsでエラー吐かれました. どうやらtwitter apiはcorsに対応していないよう...

d.hatena.ne.jp

 ↑ 古い記事ですが....

 なので素直にrails側からデータ取得してReact側に流すことにしました.

 

 

Facebook API のためのssl設定

facebook developers のサイトによると, graph api 利用にはhttpsssl通信)が必要だそう.

developers.facebook.com

 herokuにあげたら恐らく勝手にhttpsにしてくれるんだけど, localhostでやる分にはrailsはデフォルトでhttp通信になってます. なので auth/facebookにアクセスしても, アクセス元がhttpsでない?ためエラー吐かれてoauth認証出来ません.

 

そこで下の記事を参考にssl化します.

www.devmynd.com

force_sslの設定

Railsの app/config/environments/development.rb ファイルに, 

config.force_ssl = true

を記述します. こうすることで, 全てのアクセスをhttpsにしてくれるらしい. 

webpacker.ymlのssl設定

webpackはデフォルトでwebpack-dev-serverがhttp通信するそうなので, それもhttpsに変えます.

https://webpack.js.org/configuration/dev-server/#devserver-https

webpackの仕様

 webpackerでは app/config/webpacker.yml でこの設定が出来ます. 以下のように書き換えます.

dev_server:
# ここをtrueにする
https: true
host: 127.0.0.1
port: 3035
public: 127.0.0.1:3035

opensslで仮の証明書を作成

https通信を行う上ではsslサーバ証明書というものが必要になるので,  それを作成します. 本来は認証局が発行するものらしいので, ここではあくまで動かすための仮のものということで.

# コンテナ起動
docker-compose up -d

# コンテナに入る
docker exec -it my-app bash

# ssl証明書作成
openssl genrsa -out server.key 2048
openssl req -new -x509 -key server.key -out server.crt -days 365

恐らくコンテナ入らなくても docker-compose run --rm web openssl .... でいける ?

Procfileの書き直し

以上が終わったら, Procfileを書き直します.

web: bundle exec rails s Puma -b 'ssl://0.0.0.0:3000?key=server.key&cert=server.crt'

webpack: bin/webpack-dev-server --hot

rails側で動くサーバに先ほど作成したserver.key と server.crt を埋め込みます. するとhttpsで通信できるようになります. ただし

 

1. 初回アクセス時は 127.0.0.1:3000 ではアクセスできない問題

=> 今まではこれで大丈夫でしたが, 今度からは https://127.0.0.1:3000 と明示しないとうまくいかない....

2. 初回アクセス時は信頼されてないと警告が出る

=> 信用するとすればアクセスできます.

 

Graph API を開発者モードで動かす

facebookのapiであるgraph api ではユーザに求める情報(許可範囲)が適切であるか、アプリ公開前にfacebookに認証してもらわないといけないみたいです. なのでそのままでは取得できる情報が限られます.

qiita.com

 現在のアプリがユーザから取得可能な情報は

/me?fields=permissions  でわかる.

 なので開発時にはテストユーザと呼ばれるアプリを許可した架空のユーザを作成します. そのユーザからはfacebookの認証なしに任意に設定した情報を取得可能です.

 

やり方は,

1. 自分の作成したアプリを選択

2. 役割 → テストユーザ → 追加 を選択

3. このアプリのテストユーザを許可「はい」→ 取得したい情報を選択

(ex.) user_post(ユーザの投稿情報), user_photos(投稿写真)

リファレンス - Facebookログイン - ドキュメンテーション - 開発者向けFacebook

 4. 編集 → パスワード変更

 

以上でこのアカウントでrailsアプリから auth/facebook にアクセスすると無事oauth認証画面に行きます.

 

因みに, .envファイルにfacebookのパスワードの記入も忘れずに.

これから

railsサイドからjavascriptのfetchみたいなのがうまくできなくてapi叩けてないので早く叩く.

 

あと悲報なのですが, instagramのapiはビジネスなんとかの審査通さないと利用できないみたいです........... 辛い

Rails+React with webpacker ~Herokuにデプロイ~

ここまでで簡単ですが

  • twitter認証 + タイムライン表示
  • postgresqlでデータベース構築
  • devise認証
  • Rails と React 間でaxiosで通信

できたので, いったんHerokuにデプロイできるかやってみました. ので, そのメモです.

 

 

dotenv-railsの導入

herokuにデプロイするにあたり, config/secrets.yml もアップロードしないといけません. ですが, 今までここにTwitterAPI Keyとか載せていたのでどうにかしないとなということになりました.

そこでdotenv-rails環境変数を扱うことにしました.

github.com

 これを使うと, .envファイルに環境変数を書くだけで勝手にrailsのファイルから呼び出せます. ですが, ここでバージョンの問題が発生したのでそのメモ.

 

Gemfileに

group :development, :test do
gem 'dotenv-rails'
end

として docker-compose run --rm web bundle install すると自分の環境だとdotenv-rails(0.7.0)が入りました. で, 仕様通りに使っても環境変数が呼び出せないと.

色々調べていくうちに, バージョンの問題が浮上しました.

github.com

というわけで, dotenv-railsを2018年8月現在で最新のバージョン2.5.0を指定して入れ直すことにしました.

 

dockerでのGemのアンインストールとGemfile.lockの更新は以下の感じです.

$ docker-compose run --rm web bundle exec gem uninstall dotenv-rails
$ docker-compose run --rm web bundle update
$ docker-compose build

したあとで, Gemfileを以下に書き換えて

group :development, :test do
gem 'dotenv-rails', '~> 2.5.0'
end
$ docker-compose run --rm web bundle install
$ docker-compose build
// 一緒にコンテナ立ち上げるなら
// $docker-compose up -d --build

するとdotenv-railsが使えるようになります.

 

herokuにデプロイ

.gitignoreの設定

dotenv-railsを使って秘匿情報を切り分けましたが, デフォルトでは.envファイルが.gitignoreに書いてないので書きます. ついでにHerokuに上げる必要のないDockerfileとdocker-compose.ymlも載せときます.

 

Herokuにデプロイ

webpackerのデプロイに関する項目を読むと, Heroku側がいい感じにやってくれそうなので, 特別な設定なくいけます.

Heroku

Heroku installs Yarn and node by default if you deploy a Rails app with Webpacker so all you would need to do:

heroku create shiny-webpacker-app
heroku addons:create heroku-postgresql:hobby-dev
git push heroku master

github.com

 で, webpack使っているので恐らくasset pipline使わない関係で, config/environments/production.rbの config.assets.compile = false  にしないと警告出ます.

そしたらもう

$ cd 作業ディレクトリ
$ git init
$ git add -A
$ git commit -m "heroku"
$ heroku login
$ heroku create my-app
$ git push heroku master

で行けるかなと思ったのですがいくつかつまずいたのでメモ.

app/javascript/imagesに画像がなくてコンパイルエラー

webpackerのエントリポイントのファイルで require.context('images', true, /\.(png|jpg|jpeg|svg)$/) にしているのに画像がないため怒られました. 何でもいいのでこの拡張子の画像を入れておきましょう.

起動のコマンドがおかしなことになってた

今回develop環境ではforemanを使っていました. で, Procfileを書いてたんですが, ProcfileがあるとHerokuはここに書いてあるコマンドでアプリを起動させるみたいです. デフォルトだと bundle exec rails s -p $PORT -e $RAILS_ENV で起動するみたいなので, Procfileの中身を以下に書き換えます.

web: bundle exec rails s -p $PORT -e $RAILS_ENV

※ あまりよくわかっていないのですが, たとえProcfileを.gitignoreしたとしてもProcfileの中身が読み込まれます. なので, Herokuに上げる際は必ず書き換えたほうがいいでしょう.

※ もっと良いやり方あると思う...

config/environments/production.rb にdeviseメール認証の記述をしていない

開発環境では

config.action_mailer.default_url_options = { host: '127.0.0.1', port: 3000 }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:enable_starttls_auto => true,
:address => 'smtp.gmail.com',
:port => 587,
:domain => 'smtp.gmail.com',
:user_name => '*********@gmail.com',
:password => ENV["GMAIL_PASS"],
:authentication => 'login'
}

を書いていたのですが, 単純にデプロイ側にこれを書いていなかったのでエラー吐かれました. なので, hostをデプロイ環境に合わせて

config.action_mailer.default_url_options = { host: 'my-app.herokuapp.com' }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:enable_starttls_auto => true,
:address => 'smtp.gmail.com',
:port => 587,
:domain => 'smtp.gmail.com',
:user_name => '*********@gmail.com',
:password => ENV["GMAIL_PASS"],
:authentication => 'login'
}

を記述します.

twitterのコールバックURLが開発環境の時のまま

twitter developmentの登録したアプリのところで, CallbackURLに

https://my-app.herokuapp.com/auth/twitter/callback

を追加しておけば動きました.

 

以上が無事済んだら

$ git add -u
$ git commit -m "heroku v2"
$ git push heroku master

// データベース構築
$ heroku run rake db:migrate

// 各種環境変数設定
$ heroku config:set ENV_NAME="..."

// アプリ起動
$ heroku open

// ログを見たいときは
$ heroku logs -t

// データベースの情報取得
$ heroku config | grep DATABASE_URL
▽以下のような順番で出力されます.
postgres://(User)***:(Pass)***@(Host)***:(Port)***/(Database)***

でOKです. データベース情報は, PG Commander の設定に入れてやれば接続できます.

 

これから

今後もデプロイでつまずきそうなので, 気をつけながらやっていきたいです.

 

※ 追記

ちなみにdockerでもデプロイできるみたいなので, 余力があればやろうかな.

devcenter.heroku.com

 

Rails+React axiosで通信 ~CSRFトークンの設定~

前回RailsとReact間でaxiosを使ってajax通信をしたのですが, GETメソッドは特別な設定なくできました. しかし, POST(GET以外)は設定しないと動かなかったのでそのメモです.

dai7igarashi.hatenablog.com

 

 

CSRFとは

CSRFとは Cross-Site Request Forgeries の略で, ユーザの意図しない動作をさせる攻撃です.

情報処理推進機構:情報セキュリティ:脆弱性関連情報の取扱い:知っていますか?脆弱性 (ぜいじゃくせい)/3. CSRF (クロスサイト・リクエスト・フォージェリ)

 例えば, AさんがあるSNSにログイン中に悪意のあるサイトを見たとします. そして悪意のあるサイトの罠が仕掛けられたリンクをクリックすると, あたかもAさんがリクエストを出したように, 悪意のあるサイトからログイン中のSNSにリクエストが飛びます. すると例えばアカウントを削除されたりなんなり, 色々悪用されてしまうと言ったことが起こります. これがCSRFです.

 

RailsCSRF対策

RailsにはCSRF対策が施されていて, 悪意のあるサイトからのリクエストか, 自分のプログラムからのリクエストか判別できるような仕組みが用意されています. Railsでは, GETリクエストはそこまで破壊的な処理がなされないと考えているのか, 対策なくaxiosで通信できるのですが, POST, PUT, DELETEでは致命的なダメージを与えると考えているらしく, CSRFトークンと呼ばれるパスワードみたいなものの設定が必須になっています.

Rails側で用意されているヘルパーメソッド(form_forとか)にはデフォルトでこのCSRFトークンが設定されているので気にせずPOSTとかできてましたが, フロントエンドとサーバーサイドを分けて通信する場合には意識しなければなりません.

shindolog.hatenablog.com

 

上記サイトによると, 

   検証を通すには「authenticity_token をフォームに埋め込んでおく」

 「X-CSRF-Token をリクエストヘッダーに設定しておく」

らしいです.

で, 今回の場合どうするかというと, rails-ujsというajax通信をよしなに行ってくれるライブラリのcsrfトークンを取得する機能を使って解決します. 具体的には, 

import React from 'react';
import axios from 'axios';
import { csrfToken } from 'rails-ujs';

class MyApp extends React.Component {
constructor(props) {
super(props);

// この記述だけでaxiosでpostが行える.
axios.defaults.headers.common['X-CSRF-Token'] = csrfToken();
}


func1() {
axios.post('/post_url', {
name: 'Tom',
text: 'Hello World!'
})
.then(response => {
console.log(response.data);
})
.catch(err => {
console.log(err);
});
}

これで送れます. コントローラ側ではparamsにjson形式で入っています. 詳しくはbinding.pryすればわかります.

tech.medpeer.co.jp

 

今後

データベース構築かな....... 以外と一番考えなきゃいけないとこな気がします.

dockerでpry-railsを使う(デバッグ)

paramsとかみたいなーって思ったとき, dockerだと普通にpry-rails使ってbinding.pryできなかったのでそのメモです.

 

 

docker-compose で pry-rails を使う

https://stackoverflow.com/questions/35211638/how-to-debug-a-rails-app-in-docker-with-pry

 

上記の記事を参考にしました. 

# バックグラウンドで動かす
$ docker-compose up -d
$ docker ps
railsが動いてるコンテナのNAMESをみる
$ docker attach コンテナNAMES

すると, 一見何も変化がないんですが, binding.pryのとこに来るとpryしたときの, いつもの感じのコンソール画面になります. で, コンソールには表示されないんですがキーボードを打ってEnterを押すとみれます.

ちなみに抜けるときは exit で. 

Rails+React with webpacker twiiter認証してタイムライン表示

TwitterでOmniAuthやってみたのでメモしておきます.

 

Twitter認証とタイムライン表示

下記サイトを参考にしました.

reviewlog.info 

導入したgemは

 

基本的に上記サイトの通りでやりました. omniauth入れておくと, auth/twitterにリンク飛ぶとよく見るtwitter認証画面に飛びます. で, 認証するとcallbackで指定したリンクに飛ぶと...

 

つまったこととしては, twitter development(https://developer.twitter.com/)サイトのアプリ登録のとこですね. 

 

アプリ登録する際, CallbackURLを設定しますが, 上記サイト通りのルーティングだとhttp://127.0.0.1:3000/auth/twitter/callbackにしないといけません. localhostは拒否られます.

で, localで作業するとき,localhost:3000とかするとコールバックしたときにエラー吐かれるので, 最初からhttp://127.0.0.1:3000にアクセスしないといけませんでした.

 

今後

twitterプロフィール画像とかも取得したいと思います. あとdeviseとも連携させたいな.

 

追記

client = Twitter::REST::Client.new do |config|
config.consumer_key = Rails.application.secrets.twitter_consumer_key
config.consumer_secret = Rails.application.secrets.twitter_consumer_secret
config.access_token = session[:oauth_token]
config.access_token_secret = session[:oauth_token_secret]
end

した場合, ユーザの基本的な情報は,

client.user.********

の********に下記サイトのやつを引っ張ってくれば良い.

GET account/verify_credentials — Twitter Developers

 (ex.) id なら client.user.id

Rails+React with webpacker データベースをReactでいじる

前回まででReactをRailsで使う環境は整いました.

でも, Reactでデータベースの値をどうやって参照すればいいのかつまづいたのでメモしておきます.

 

 

ルーティングとコントローラ設定, axiosでReactと通信

webpackerのファイルの構成は割愛します. 自分は app -> javascript 配下に

  • packs : webpackerのデフォルトのエントリポイント
  • javascripts : Reactのコンポーネント( jsx )
  • stylesheets : css

のような感じで置きました.

そして docker-compose run --rm web rails g controller home でhomeコントローラ作成しました.

そして, app/config/routes.rb を以下のように設定.

Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
root 'home#index'
get 'axios_get' => 'home#getting'
end

こうすることで, Reactコンポーネントからaxiosで /axios_get にアクセスするとhomeコントローラのgettingメソッドが呼ばれる. なのでgettingメソッドのから値を返してやれば良い. homeコントローラは以下.

class HomeController < ApplicationController

def index
@todos = Todo.all
# app/viwes/home/index.html.erbを表示.
# だけど今後は表示を全てReactコンポーネント化する予定なので不要.
render template: 'home/index', layout: 'application'
end

def getting
@todos = Todo.all
render json: @todos, layout:'application'
end

end

ここではTodoというモデル(todosテーブル)を作ってデータベースの値を引っ張ってきています. で, 返り値はjson形式です. 

(一応layout指定してますが, 無くても勝手に読んでくれる???)

 

最後にReact側で

import React from 'react';
import axios from 'axios';

class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
this.hundleClick = this.hundleClick.bind(this);
}

hundleClick() {
let num = this.state.count + 1;
this.setState({count: num});

axios.get('axios_get')
.then(response => {
console.log(response.data);
})
.catch(err => {
console.log(err);
});
}

render() {
return(
<div>
<p className="MyApp-count">Count : {this.state.count}</p>
<button type="button" className="btn btn-primary" onClick= {this.hundleClick}>Button!</button>
</div>
);
}

}

export default MyApp;

みたいに書けばコンソールのログにデータベースの値がJSONで入ってます.

 

※ 初歩的ですが, Reactのクラス名は大文字英字で始めないとエラーになります(一回やった笑)

 

これから

次はデータの取り扱い方を詳しく見ていこうかなと. あとpostもしようかなと.