はてなOAuthがちょっと使いにくいなと思った話。
iOSではてなフォトライフに写真をアップロードするアプリを作りたいなと考えています。
はてなOAuthを使うのに最適な方法を考えているのですが思い付きませんでした。実際の最適解がよくわからないのでとりあえず実現だけさせてみました。
はてなOAuthのコールバックについて
サードパーティー製Twitterクライアントなど、よくあるOAuthを使うアプリの場合にはざっと下記のような順序です。
- アプリからSafariを起動する(あるいはSFSafariViewControllerを起動する)
- サービスにログインする
- コールバックでカスタムURLスキーマを叩いて元のアプリに戻ってくる
- アプリでログイン後の機能が使えるようになる
僕もこんな感じでログイン画面を作ろうと考えていました。
はてなOAuthも当然ですがコールバックを指定することができます。
2.2. で ユーザーがアクセスを許可した場合ははてなはユーザーを 2.1. の oauth_callback で指定されたURLに oauth_verifier パラメータを付けてリダイレクトします。また URL の代わりに "oob" が指定されていた場合は oauth_verifier の値をユーザーに表示しますので、ユーザーに入力を指示して下さい。
はてなOAuthのコールバックで指定できるものは下記の2つだとわかりました。
- URL
- oob
このURLが少しネックでphotouploader://hoge
みたいなURLのコールバックを叩いてもらおうと思うもののエラーが発生してしまいました。悶々と悩んだ結果実際に使えるのは下記の2つだとわかりました。
- http(https) から始まるURL
- oob
はてなOAuthを作っているひと的には
- コールバックに
oob
を指定して、Safariで認証してもらってアプリに戻ってアクセスコードを入力する - コールバックに
http://適当なURL/
を指定して、WebView画面で認証後にURLのドメイン部分を監視してフックする
のどちらかを想定しているのかと思いました。おそらく前者かな?
App Storeに並んでいるアプリで、はてなへのログインを促しているものは後者の実装が多いようです。Safariで認証させてからアプリを起動することはできないようです。
はてなのログイン画面を作ってみた
ここでは後者の方法ではてなのログイン画面を作ってみました。かなりざっくりとですがコードを抜粋しました。認証のためにOAuthSwift
を使いました。
static func login(parentViewController: UIViewController, consumerKey: String, consumerSecret: String) -> Future<Credential, NSError> { let promise = Promise<Credential, NSError>() let oauthswift = OAuth1Swift( consumerKey: consumerKey, consumerSecret: consumerSecret, requestTokenUrl: "https://www.hatena.com/oauth/initiate", authorizeUrl: "https://www.hatena.ne.jp/touch/oauth/authorize", accessTokenUrl: "https://www.hatena.com/oauth/token" ) let viewController = UIStoryboard.init(name: "LoginWebView", bundle: nil).instantiateInitialViewController() as! LoginWebViewController viewController.previousViewController = parentViewController oauthswift.authorize_url_handler = viewController oauthswift.authorizeWithCallbackURL( NSURL(string: "http://photouploader.example.jp/")!, success: { credential, response, parameters in let c = Credential() c.oauth_token = credential.oauth_token c.oauth_token_secret = credential.oauth_token_secret promise.success(c) }, failure: { error in promise.failure(HatenaError()) } ) return promise.future }
LoginWebViewController(と名付けたWebView画面)でログインしたあとにhttp://適当なURL/
に遷移するのを見張るようにしました。
class LoginWebViewController: OAuthWebViewController { @IBOutlet weak var toolbar: Toolbar! @IBOutlet weak var webView: UIWebView! var targetURL : NSURL = NSURL() var previousViewController: UIViewController? override func viewDidLoad() { super.viewDidLoad() // もろもろ省略 loadAddressURL() } // MARK:- override func doHandle(url: NSURL) { previousViewController?.navigationController?.pushViewController(self, animated: true) } // MARK:- override func handle(url: NSURL) { targetURL = url super.handle(url) loadAddressURL() } }
extension LoginWebViewController: UIWebViewDelegate { func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { print("\(request.URL)") if let url = request.URL where (url.host == "photouploader.example.jp"){ OAuthSwift.handleOpenURL(url) self.dismissWebViewController() return false } return true } }
これでログインするところまではいけると思います。
一番最初に書いた通りはてなOAuthを使ったログイン画面の最適解について悶々と考えた結果、アプリ本体に手をつける前に投げ出してしまいました。気が向いたらまた着手します。