はじめに
今、作ってるアプリとサーバサイドでSFSafariViewControllerでTwitterOAuthしてアプリ側にコールバックさせたいっていうことで実装しました。
環境
クライアント側
サーバサイド側
- Python 3.6.2
- Django==2.0
- python-social-auth==0.3.6
- social-auth-app-django==2.1.0
- social-auth-core==1.6.0
概要
PCからのアクセスを全て遮断するために独自のURLを作成してTwitterログイン画面へのリダイレクトをさせています。
サーバサイド側のURLの説明
BASE_URL = 'http://hostname:8000/'
URL | 説明 |
---|---|
api/user/login |
ログイン処理 |
accounts/login/twitter |
TwitterログインURLを生成してリダイレクト |
accounts/complete/twitter |
Twitterログインコールバック後のURL |
実装
クライアント側
URLスキーマの設定
今回は app-callback://
を指定するとアプリが開くようにしました。
ViewController.swift
import UIKit import SafariServices class ViewController: UIViewController { static let kCloseSafariViewController = NSNotification.Name(rawValue: "kCloseSafariViewControllerNotification") private var safari: SFSafariViewController! private var hasToken = false override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(safariLogin(_:)), name: ViewController.kCloseSafariViewController, object: nil) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if hasToken { return } // ログイン画面の表示 let url = URL(string: "http://hostname:8000/user/login")! safari = SFSafariViewController(url: url) present(safari, animated: true, completion: nil) } deinit { NotificationCenter.default.removeObserver(self, name: ViewController.kCloseSafariViewController, object: nil) } @objc func safariLogin(_ notification: NSNotification) { guard let url = notification.object as? URL else { return } print(url.absoluteString) hasToken = true safari.dismiss(animated: true, completion: nil) } }
AppDelegate.swift
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? ・・・ func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool { if let sourceApplication = options[.sourceApplication] as? String { # SFSafariViewControllerからのものであるか if String(describing: sourceApplication) == "com.apple.SafariViewService" { NotificationCenter.default.post(name: ViewController.kCloseSafariViewController, object: url) return true } } return true } }
サーバサイド側
詳しい実装は、今回は省略します。多分調べてもらったら沢山出てくると思う。
上記リンク、詳しくは見てないけど、書いてあったので貼っておきます。
views.py
from django.core.exceptions import DisallowedRedirect from django.http.response import HttpResponseForbidden from django.views.generic import TemplateView class TwitterLoginView(TemplateView): template_name = 'login.html' def get(self, request, *args, **kwargs): # 初回アクセス if '_auth_user_id' not in request.session and request.user.is_anonymous: return super().get(request, *args, **kwargs) ・・・ # ログイン処理とか try: self.template_name = 'success.html' kwargs['hoge'] = 'nnsnodnb' # サンプルパラメータだよ return super().get(request, *args, **kwargs) except DisallowedRedirect: # 一応戻しておく self.template_name = 'login.html' logout(request) return HttpResponseForbidden()
api/urls.py
startapp
で作成した api
の配下に urls.py
を作成しています。
from django.urls import path from .views import TwitterLoginView app_name = 'api' urlpatterns = [ ・・・ path('user/login', TwitterLoginView.as_view(), name='twitter_login_view'), ]
project/urls.py
from django.contrib import admin from django.contrib.auth.decorators import login_required from django.urls import include, path from rest_framework_swagger.views import get_swagger_view schema_view = get_swagger_view(title='Hogehoge API') urlpatterns = [ ・・・ path('accounts/', include('social.apps.django_app.urls', namespace='social')), path('api/', include('api.urls', namespace='api')), ]
login.html
JavaScriptの href
を使用してログイン画面に遷移させます。
URLのパラメータに next
を指定することで `accounts/complete/twitter
が呼ばれた後のリダイレクト先を指定できます。
今回は自分自身にリダイレクトさせます。
<script type="text/javascript"> location.href = "{% url 'social:begin' 'twitter' %}?next={% url 'api:twitter_login_view' %}"; </script>
success.html
なぜか django.shortcuts.redirect
で app-callback://
を指定しても DisallowedRedirect
がSFSafariViewControllerが呼ばれてしまったので敢えて作りました。
context
を設定してればJavaScriptであろうと見られるのでクエリパラメータ追加しておきます。
<script type="text/javascript"> location.href = "app-callback://?hoge={{ hoge }}"; </script>
動作サンプル
わかりづらいですが、Twitterログイン画面での青い ログイン ボタンしか押していないです。
最後に
意外と簡単に実装できたので、 Twitterアプリが入ってないときの TwitterKit
等でされている実装を再現できるようになりました。
今回は、Twitterアプリは最新のものが入っていますが、どうしてもブラウザ経由でないといけないという制約があったので、SFSafariViewControllerで実装しました。
もしやる気があったらいつか BitBucket
にでもサンプルをアップロードしようと思います。(多分しない)