/
atom.xml
504 lines (388 loc) · 16 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Category: devise | @znz blog]]></title>
<link href="http://blog.n-z.jp/blog/categories/devise/atom.xml" rel="self"/>
<link href="http://blog.n-z.jp/"/>
<updated>2014-04-09T09:49:24+09:00</updated>
<id>http://blog.n-z.jp/</id>
<author>
<name><![CDATA[Kazuhiro NISHIYAMA]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[doorkeeper gem の API のクライアント]]></title>
<link href="http://blog.n-z.jp/blog/2013-10-08-doorkeeper-api-client.html"/>
<updated>2013-10-08T21:06:00+09:00</updated>
<id>http://blog.n-z.jp/blog/doorkeeper-api-client</id>
<content type="html"><![CDATA[<p><a href="http://rubygems.org/gems/doorkeeper">doorkeeper gem</a>
で API を作る方は
<a href="https://github.com/applicake/doorkeeper-provider-app">doorkeeper-provider-app</a>
というサンプルの
<code>app/controllers/api/</code>
以下などをみればすぐにわかったのですが、
API を呼び出す方は
<a href="https://github.com/applicake/doorkeeper/wiki/Create-a-OmniAuth-strategy-for-your-provider">OmniAuth の中でユーザーの情報を取り出す</a>
だけならすぐに出来たのですが、
コントローラーの中など呼び出す方法は
<a href="https://github.com/applicake/doorkeeper-devise-client">doorkeeper-devise-client</a>
を見てもよくわからなかったので、まとめてみました。</p>
<!--more-->
<h2>動作確認バージョン</h2>
<ul>
<li>provider 側
<ul>
<li>rails 3.2.14</li>
<li>doorkeeper 0.7.3</li>
</ul>
</li>
<li>client 側
<ul>
<li>rails 4.0.0</li>
<li>devise 3.1.1</li>
<li>omniauth 1.1.4</li>
<li>omniauth-oauth2 1.1.1</li>
<li>oauth2 0.8.1</li>
</ul>
</li>
</ul>
<h2>準備</h2>
<p>まず
<a href="https://github.com/applicake/doorkeeper/wiki/Create-a-OmniAuth-strategy-for-your-provider">Create a OmniAuth strategy for your provider</a>
を参考にして、
OmniAuth の中で
<code>access_token.get('/api/v1/me.json').parsed</code>
は出来るところまでは準備しておきます。</p>
<p>目的としては、
この
<code>access_token</code>
が認証より後で呼ばれる他のコントローラーの中で取得できれば良いということになります。</p>
<p>余談ですが、
doorkeeper の wiki は
<a href="https://github.com/applicake/doorkeeper/wiki/Supported-Ruby-&-Rails-versions">Supported Ruby & Rails versions</a>
のように情報が古いまま放置されているページもあるようなので、
<a href="https://github.com/applicake/doorkeeper">README</a>
などのソースコード側のドキュメントも参照した方が良さそうです。</p>
<h2>必要なもの</h2>
<p><code>access_token</code>
は
<code>OAuth2::AccessToken</code>
クラスのオブジェクトです。</p>
<p>生成するには</p>
<ul>
<li><code>OAuth2::Client</code> のオブジェクト</li>
<li>認証で取得した <code>token</code></li>
</ul>
<p>が必要になります。</p>
<p><code>OAuth2::Client</code>
の生成には</p>
<ul>
<li><code>client_id</code></li>
<li><code>client_secret</code></li>
<li>URL</li>
</ul>
<p>が必要になります。</p>
<h2>token の保存</h2>
<p>まず
<code>OAuth2</code>
の認証で取得した
<code>token</code>
を保存しておく必要があります。</p>
<p><code>Users::OmniauthCallbacksController#doorkeeper</code>
で
<code>session[:doorkeeper_token] = request.env["omniauth.auth"]["credentials"]["token"]</code>
のようにしてセッションなどの後で使える場所に保存しておきます。</p>
<p>後で調べてわかったのですが、
<a href="https://github.com/applicake/doorkeeper-devise-client/blob/master/app/controllers/users/omniauth_callbacks_controller.rb">doorkeeper-devise-client の Users::OmniauthCallbacksController</a>
では
<code>request.env["omniauth.auth"].credentials.token</code>
を
<code>user.doorkeeper_access_token</code>
でデータベースに保存していました。</p>
<h2>OAuth2::Client の作成</h2>
<p><a href="https://github.com/applicake/doorkeeper-devise-client/blob/master/app/controllers/application_controller.rb">doorkeeper-devise-client の ApplicationController</a>
では必要な情報は定数経由で受け取るようになっていました。</p>
<p>今回は
<code>devise</code>
と
<code>omniauth-oauth2</code>
を使っているので、
その情報を使って生成するようにしました。
要点だけまとめると以下のコードになります。</p>
<p>```ruby</p>
<pre><code>config = Devise.omniauth_configs[:doorkeeper]
strategy = config.strategy_class.new(*config.args)
client = strategy.client
</code></pre>
<p>```</p>
<h2>OAuth2::AccessToken の生成</h2>
<p>ここまで準備ができれば後は
<code>OAuth2::AccessToken.new</code>
するだけです。</p>
<p>まとめると以下のコードになります。</p>
<p>```ruby
def access_token</p>
<pre><code>return @access_token if defined?(@access_token)
config = Devise.omniauth_configs[:doorkeeper]
strategy = config.strategy_class.new(*config.args)
token = session[:doorkeeper_token]
@access_token = OAuth2::AccessToken.new(strategy.client, token)
</code></pre>
<p> end
```</p>
<h2>API 呼び出し</h2>
<p><code>access_token</code>
が出来たら後は呼び出しに使うだけです。</p>
<p>単純な情報取得は
<code>get</code>
して
JSON
なら
<code>parsed</code>
を呼び出すだけです。</p>
<p><code>ruby
access_token.get("/api/v1/me.json").parsed
access_token.get("/api/v1/posts.json").parsed
</code></p>
<p>今回は連携して書き込みたいというのが目的だったため、
<code>post</code>
も使いました。</p>
<p>モデルの例としては
<code>rails g scaffold post title body:text</code>
で API の提供側では以下の実装とします。</p>
<p>```ruby app/controllers/api/v1/posts_controller.rb
module Api::V1
class PostsController < ApiController</p>
<pre><code>doorkeeper_for :index
doorkeeper_for :create
respond_to :json
def index
respond_with Post.all
end
def create
post = Post.new(params[:post])
post.user = current_resource_owner
post.save!
respond_with post
end
</code></pre>
<p>```</p>
<p>呼び出し側は以下のようになります。</p>
<p><code>ruby
access_token.post("/api/v1/posts", params: { post: { title: title, body: body } })
</code></p>
<p><code>params</code>
による指定は
<a href="https://github.com/intridea/oauth2/blob/master/lib/oauth2/access_token.rb">OAuth2::AccessToken</a>
のソースをみて推測しました。</p>
<h2>scope 付き API 提供</h2>
<p>書き込みも許可すると
<code>scope</code>
を分けたくなります。</p>
<p>doorkeeper 側では
<a href="https://github.com/applicake/doorkeeper/wiki/Using-Scopes">Using Scopes</a>
を参考にして</p>
<ul>
<li>initializers に scopes 追加</li>
<li>翻訳追加</li>
<li>API に scopes 追加</li>
</ul>
<p>をしておきます。</p>
<p><code>ruby config/initializers/doorkeeper.rb
default_scopes :public
optional_scopes :admin, :write
</code></p>
<p>2013-12-20 追記:
doorkeeper gem を 0.7.3 から 0.7.4 に上げたところ、
シンボルだとうまく動かなくなってしまったので、
文字列に変更しました。</p>
<p>(doorkeeer gem 0.7.3 以前)
```ruby app/controllers/api/v1/posts_controller.rb</p>
<pre><code>doorkeeper_for :index, :show, scopes: [:public]
doorkeeper_for :create, :update, scopes: [:admin, :write]
</code></pre>
<p>```</p>
<p>(doorkeeer gem 0.7.4 以降)
```ruby app/controllers/api/v1/posts_controller.rb</p>
<pre><code>doorkeeper_for :index, :show, scopes: %w"public"
doorkeeper_for :create, :update, scopes: %w"admin write"
</code></pre>
<p>```</p>
<p>参考のため、この API の rspec も載せておきます。
複数の <code>scopes</code> を設定する時に <code>,</code> 区切りだとうまくいかないところがあったので、
スペース区切りにしています。</p>
<p>```ruby spec/controllers/api/v1/posts_controller_spec.rb
require ‘spec_helper’</p>
<p>describe Api::V1::PostsController do
describe “GET ‘index’” do</p>
<pre><code>let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "http://app.com") }
let!(:user) { FactoryGirl.create(:normal_user) }
let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "public" }
subject { response }
context "valid token" do
before do
get 'index', format: :json, access_token: token.token
end
it { should be_success }
its(:status) { should eq(200) }
its(:body) { should == Post.all.to_a.to_json }
end
context "invalid token" do
before do
get 'index', format: :json, access_token: token.token.succ
end
it { should_not be_success }
its(:status) { should eq(401) }
end
</code></pre>
<p> end</p>
<p> describe “GET ‘index’ without scopes” do</p>
<pre><code>let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "http://app.com") }
let!(:user) { FactoryGirl.create(:normal_user) }
let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "write" }
subject { response }
context "valid token" do
before do
get 'index', format: :json, access_token: token.token
end
it { should_not be_success }
its(:status) { should eq(401) }
its(:body) { should == " " }
end
</code></pre>
<p> end</p>
<p> describe “POST ‘create’” do</p>
<pre><code>let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "http://app.com") }
let!(:user) { FactoryGirl.create(:normal_user) }
let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "public write" }
subject { response }
context "valid token" do
before do
post 'create', format: :json, access_token: token.token, post: { title: "title", body: "some content" }
end
it { should be_success }
its(:status) { should eq(201) } # 201 Created
its(:body) { should == Post.last.to_json }
end
context "invalid token" do
before do
post 'create', format: :json, access_token: token.token.succ
end
it { should_not be_success }
its(:status) { should eq(401) }
end
</code></pre>
<p> end
end
```</p>
<h2>scope 付き呼び出し</h2>
<p><code>devise</code>
の設定で
<code>omniauth</code>
の設定に
<code>scope</code>
を追加するだけです。</p>
<p><code>ruby config/initializers/devise.rb
config.omniauth :doorkeeper, ENV['DOORKEEPER_APP_ID'], ENV['DOORKEEPER_APP_SECRET'], { scope: 'public write' }
</code></p>
<p>rspec のところでもちょっと書きましたが、
区切りが <code>,</code> だとうまくいかないことがあったので、
スペース区切りにしています。</p>
<p>原因は
<a href="https://github.com/applicake/doorkeeper/blob/master/lib/doorkeeper/oauth/scopes.rb">lib/doorkeeper/oauth/scopes.rb</a>
で
<code>string.split</code>
のように無引数の
<code>String#split</code>
を使っているからではないかと推測していますが、確認はしていません。</p>
<h2>まとめ</h2>
<p><code>client_id</code> と <code>client_secret</code> と provider の URL はあらかじめ用意しておいて、
client 側の rails アプリに設定しておきます。</p>
<p><code>token</code>
は OAuth2 で取得したものを
<code>session</code>
やデータベースなどに保存しておいて使います。</p>
<p>必要なら
<code>scopes</code>
も設定できます。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[devise 3.0.3 と devise 3.1.0 で自動生成されるファイルの違い]]></title>
<link href="http://blog.n-z.jp/blog/2013-09-18-devise30-devise31.html"/>
<updated>2013-09-18T15:09:00+09:00</updated>
<id>http://blog.n-z.jp/blog/devise30-devise31</id>
<content type="html"><![CDATA[<p><a href="https://gist.github.com/znz/6603471">devise 3.0.3 と devise 3.1.0 で自動生成されるファイルの差分</a>
をとって、バージョンアップした時に対応が必要な場所を調べました。</p>
<!--more-->
<h2>対応が必要な場所</h2>
<p>最初に結論をまとめておきます。</p>
<ul>
<li><code>config/initializers/devise.rb</code> に <code>config.secret_key</code> を追加。
<ul>
<li>これは <code>rails</code> コマンドを実行しようとした時などにエラーとどういう内容を追加すれば良いのかが出るのですぐに気付きます。</li>
</ul>
</li>
<li><code>rails generate devise:views</code> をしていたのなら
<ul>
<li><code>app/views/devise/mailer</code> の <code>token</code> 周り</li>
<li><code>app/views/devise/shared/_links.erb</code> の <code>devise_mapping.recoverable?</code> の行
の修正が必要です。</li>
</ul>
</li>
<li><code>app/models/user.rb</code> の <code>:token_authenticatable</code> の削除の検討
<ul>
<li>削除しなくても動いているので、すぐに削除する必要はなさそうですが、
<code>rails generate devise User</code> で生成される例からなくなっているので
削除を検討した方が良さそうです。</li>
</ul>
</li>
<li>データベースのテーブルの変更に追従するなら
<code>migration</code> を作って対応すれば良さそうです。</li>
</ul>
<h2>対象バージョン</h2>
<p>rails 3.2.14 との組み合わせで devise 3.0.3 と devise 3.1.0 で比較しました。
細かいバージョンは
<a href="https://gist.github.com/znz/6603471#file-bundle-list-in-devise30-txt">devise 3.0.3 の時の bundle list</a>
と
<a href="https://gist.github.com/znz/6603471#file-bundle-list-in-devise31-txt">devise 3.1.0 の時の bundle list</a>
を参照してください。</p>
<h2><code>rails generate devise:install</code> の差分</h2>
<p><code>rails generate devise:install</code> の差分のうち、
<a href="https://gist.github.com/znz/6603471#file-initializer-diff">config/initializers/devise.rb の差分</a>
は <code>"</code> から <code>'</code> への変更などもあって本質的ではない部分も多かったので、
<a href="https://gist.github.com/znz/6603471#file-initializer-tr-diff">tr で処理した差分</a>
を作ってみたところ、本質的には
<code>config.secret_key =</code>
の行の追加だけでした。</p>
<p><a href="https://gist.github.com/znz/6603471#file-locale-diff">config/locales/devise.en.yml の差分</a>
もあるので、
<code>locales</code>
のファイルも更新しておくと良さそうです。</p>
<h2><code>rails generate devise User</code> の差分</h2>
<p><code>rails generate devise User</code> の差分として、
<a href="https://gist.github.com/znz/6603471#file-devise_create_users-diff">migration の差分</a>
は
<code>sign_in_count</code> と <code>failed_attempts</code> への <code>NOT NULL</code> 制約の追加と
Token authenticatable 用の <code>authentication_token</code> の削除なので、
対応する変更をする <code>migration</code> を作って対応するか、
気にせず古いまま使えば良さそうです。</p>
<p><code>app/models/user.rb</code> からもコメントの中のモジュール例にあった
<code>token_authenticatable</code> が削除されています。</p>
<p><code>config/routes.rb</code> に追加される <code>devise_for :users</code> は同じでした。</p>
<h2><code>rails generate devise:views</code> の差分</h2>
<p><a href="https://gist.github.com/znz/6603471#file-views-diff">app/views の差分</a>
は
<code>@resource.なんとか_token</code>
だったものが
<code>@token</code>
に変わっているのと、
<code>app/views/devise/shared/_links.erb</code>
の
<code>if devise_mapping.recoverable? && controller_name != 'passwords'</code>
が
<code>if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'</code>
に変わっているのが
主な変更でした。</p>
]]></content>
</entry>
</feed>