LWP::UserAgentでイカリング(ニンテンドーネットワーク)の認証を突破する。

イカリングまでの認証をWWW::Mechanizeでやっていたのですが、ページ構成変わった時に対応できないよなーと思ったので、LWP::UserAgentで認証が突破できるようにしました。
参考にしたのは以下のサイトです。
<mini> Miiverse が楽しすぎて… – モノトーンの伝説日記
セッション管理をcookieでやってるようですが、ヘッダにトークンを指定してアクセスとかそういうのはできないものでしょうか。MiiverseもそうだけどAPI公開してくれると楽しいのになぁ。

手順としては以下のことをやっています。

  1. ログイン用のURLにアクセスして認証用のURLを取得
  2. 認証用のURLに認証に必要なパラメータを付与。アクセス。
  3. 返ってきたコールバックURLにアクセス

セッション管理をcookieでやっているので、cookie_jarを設定してクッキーをぱくぱくもぐもぐできるようにしています。クッキー容器っていう名称いいですね。

#!/usr/bin/perl

use warnings;
use strict;
 
use LWP::UserAgent;

my $ua = LWP::UserAgent->new;
$ua->cookie_jar({file =>"cookie.txt", autosave=>1});

my $res = $ua->post("https://splatoon.nintendo.net/users/auth/nintendo");
# 認証用のURLとパラメータを取得
my $location = $res->header("location");

# 認証に必要なパラメータの組み立て
my $dummy_url = URI->new;
$dummy_url->query_form(
"nintendo_authenticate" => "",
"nintendo_authorize" => "",
"scope" => "",
"lang" => "ja-JP" ,
"username" => "ニンテンドーネットワークのid",
"password" => "ニンテンドーネットワークのパスワード"
);

# 認証用のURLにパラメータをくっつける
my $url = URI->new($location.$dummy_url->query);
# 認証用URLにアクセスしてコールバックURLを取得
my $res_auth = $ua->post($url);
my $location_auth = $res_auth->header("location");

# コールバックURLにアクセス
$ua->get($location_auth);

# 目当てのページにアクセス
my $res_login = $ua->get("https://splatoon.nintendo.net/ranking");
print $res_login->content;

Mojoliciousのプレースホルダでドットを含むパスをキャプチャしたくない時の話。

全然別のタイトルで記事書いたのですが、内容が間違いまくってたのと愚痴っぽくてひどかったので消しました。30分位で消したから多分誰も見てないんじゃないでしょうか。

Mojoliciousの通常のプレースホルダはドットとスラッシュをキャプチャせず、含んでいた場合はルーティングに失敗します。が、その挙動を期待していたのに、ルーティングに失敗せずに200OKを返してしまうパターンがあってはまりました。
具体的に言うと、/standard/:nameのようなルートを設定していた場合、/standard/hello.htmlとか、hello.jsonのようなパスを設定されると、普通に200OKが返ってしまいます。
タチが悪いことに、param(‘name’)の値はhelloを返すので、paramの値で分岐するようなコードにしてると、意図しないルートをガード出来てないことになかなか気づきません。
具体例は以下。Perlはv5.16.3、Mojoliciousはv7.05です。

#!/usr/bin/perl

use strict;
use warnings;
use utf8;

use Mojolicious::Lite;

# リラックスプレースホルダ
get '/relax/#name' => sub{
        my $self = shift;
        return $self->render(json =>{param => $self->param('name'), format => $self->stash('format')});
} => 'r';
# /relax/hello.json:
# {"param":"hello.json","format":null}

# 普通のプレースホルダ
get '/standard/:name' => sub{
        my $self = shift;
        return $self->render(json =>{param => $self->param('name'), format => $self->stash('format')});
} => 's';
# /standard/hello.json: 
# {"format":"json","param":"hello"}

# 普通のプレースホルダ+format無効化
get '/standard_disable_format/:name' => [format => 0] => sub{
        my $self = shift;
        return $self->render(json =>{param => $self->param('name'), format => $self->stash('format')});
} => 'sd';
# /standard_disable_format/hello.json:
# status:404

普通のプレースホルダでドットを含んだ値を絶対キャプチャしたくない場合は、[format => 0]を指定しようと思いました。
MojoliciousとかMojolicious::Lite側の仕様は以下。
Mojolicious::Guides::Routing – Routing requests
Mojolicious::Lite – search.cpan.org

splapiはいろいろあってこのルートはガードする必要があるので、v1.12で修正しています。