Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Python Social Authで学ぶ、OAuth2.0認可コードフローにおける異常系への対処

Python Social Authで学ぶ、OAuth2.0認可コードフローにおける異常系への対処

PyCon JP 2022の登壇資料です
https://www.youtube.com/watch?v=8bzKf6BwDos

Yuuki Takahashi

October 15, 2022
Tweet

Other Decks in Technology

Transcript

  1. Python Social Authで学ぶ、

    OAuth2.0認可コードフローにおける

    異常系への対処

    @yktakaha4 / Yuuki Takahashi

    2022/10/15

    PyCon JP 2022


    View full-size slide

  2. 自己紹介

    2


    View full-size slide

  3. - 自分が実務で困ったり混乱した部分について、今後同じ問題に悩む人が減るとよい
    と思って明文化したものです

    - 認証 / 認可の専門家でなく、Webアプリケーション開発者としての個人的な経験に基づきます 






    - 精度向上のため、フィードバックお待ちしています🙏

    - わかりづらい部分があった 

    - 変なこと / 間違ったことを言っていた 

    - こういうことも知りたかった...などなど 

    本セッションの目的

    OAuthにおける異常系処理について一緒に考えましょう

    3


    View full-size slide

  4. Python Social Authを使ったソーシャルログイン機能の実装 

    4


    View full-size slide

  5. Python Social Authを使ったソーシャルログイン機能の実装

    そもそもソーシャルログインってなんだっけ?

    5


    View full-size slide

  6. Python Social Authを使ったソーシャルログイン機能の実装

    そもそもソーシャルログインってなんだっけ?

    6


    View full-size slide

  7. Python Social Authを使ったソーシャルログイン機能の実装

    そもそもソーシャルログインってなんだっけ?

    7


    View full-size slide

  8. Python Social Authを使ったソーシャルログイン機能の実装

    そもそもソーシャルログインってなんだっけ?

    8

    連携SNSでアカウント作成していればすぐログインできる
    氏名やメールアドレスなどの情報が自動入力される場合も

    View full-size slide

  9. Python Social Authを使ったソーシャルログイン機能の実装

    9

    😍

    最高かよ


    View full-size slide

  10. Python Social Authを使ったソーシャルログイン機能の実装

    10

    🙄

    でも作るの難しいんでしょう?


    View full-size slide

  11. Python Social Authを使ったソーシャルログイン機能の実装

    - https://github.com/python-social-auth/social-core

    - ソーシャルログインに関する共通的な機能やインターフェースを提供 

    - IdP(認証プロバイダ)毎の固有実装 

    - GitHub / Google / Twitter / etc... 

    - 処理フロー中に生じるエラーを抽象化してくれる 

    - あとで説明します


    - https://github.com/python-social-auth/social-app-django

    - social-coreをDjangoに組み込むための各種機能を提供 

    - Model / View / Middleware / etc... 

    Python Social Authを試してみる

    11


    View full-size slide

  12. Python Social Authを使ったソーシャルログイン機能の実装

    - https://github.com/yktakaha4/learn-oauth-errors-with-python-social-auth

    サンプルアプリケーションをつくってみる

    12


    View full-size slide

  13. - GitHubの設定画面からOAuthアプリケーションとして登録

    Python Social Authを使ったソーシャルログイン機能の実装

    サンプルアプリケーションの実装

    13

    Secret

    View full-size slide

  14. - いくつかの定数と、






    - URL設定をしたら…


    Python Social Authを使ったソーシャルログイン機能の実装

    サンプルアプリケーションの実装

    14

    AUTHENTICATION_BACKENDS = (
    # GitHub認証をバックエンドとして追加
    "social_core.backends.github.GithubOAuth2"
    ,
    "django.contrib.auth.backends.ModelBackend"
    ,
    )
    # GitHubから発行されたクライアント
    IDとシークレットを設定
    SOCIAL_AUTH_GITHUB_KEY = environ.get(
    "SOCIAL_AUTH_GITHUB_KEY"
    )
    SOCIAL_AUTH_GITHUB_SECRET = environ.get(
    "SOCIAL_AUTH_GITHUB_SECRET"
    )
    urlpatterns = [
    path("social/", include("social_django.urls"
    , namespace="social")),
    ]
    Secret

    View full-size slide

  15. Python Social Authを使ったソーシャルログイン機能の実装

    サンプルアプリケーションの実装

    15


    View full-size slide

  16. Python Social Authを使ったソーシャルログイン機能の実装

    サンプルアプリケーションの実装

    16


    View full-size slide

  17. Python Social Authを使ったソーシャルログイン機能の実装

    サンプルアプリケーションの実装

    17


    View full-size slide

  18. Python Social Authを使ったソーシャルログイン機能の実装

    18

    🤗

    なんか知らんが動いた〜ハッピ〜


    View full-size slide

  19. Python Social Authを使ったソーシャルログイン機能の実装

    19

    🧽

    SEVERAL DAYS LATER...


    View full-size slide

  20. Python Social Authを使ったソーシャルログイン機能の実装

    20

    エラー監視サービスに謎の通知が


    View full-size slide

  21. Python Social Authを使ったソーシャルログイン機能の実装

    21

    ⚰


    View full-size slide

  22. Python Social Authを使ったソーシャルログイン機能の実装

    - SocialAuthBaseExceptionを継承した例外がたくさん用意されている

    - AuthException / AuthFailed / AuthCanceled / AuthUnknownError / AuthTokenError /
    AuthMissingParameter / AuthAlreadyAssociated / WrongBackend / NotAllowedToDisconnect /
    AuthStateMissing / AuthStateForbidden / AuthTokenRevoked / AuthUnreachableProvider… 

    - ????????? 


    - Middlewareで上記例外を捕捉すればよいらしい

    - SocialAuthExceptionMiddleware をそのまま追加 or 継承して追加 

    エラーハンドリングを追加

    22

    class CustomizedSocialAuthExceptionMiddleware(SocialAuthExceptionMiddleware):
    def get_message(self, request, exception):
    if isinstance(exception, AuthException):
    # TODO: 本当はここで適切なエラーメッセージを表示したい
    pass
    return f"ログインに失敗しました(例外
    : {exception.__class__.
    __name__})"

    View full-size slide

  23. Python Social Authを使ったソーシャルログイン機能の実装

    エラーハンドリングを追加

    23

    このエラーがなんだったのか、
    解決のためにユーザーに何をしてもらうべきか はわからず…

    View full-size slide

  24. Python Social Authを使ったソーシャルログイン機能の実装

    24

    😂

    なんもわからんけど直った〜よかった〜


    View full-size slide

  25. (とならないために、)OAuthのおさらい

    25


    View full-size slide

  26. OAuthのおさらい

    - ユーザーが所有している権限をサードパーティーのアプリケーションへ

    認可(Authorization)する手段を標準化したプロトコル

    - 現在の最新はOAuth2.0(RFC6749) だが、OAuth2.1もドラフト策定中 


    - 認可≠認証(Authentication)

    - OAuthを用いたソーシャルログイン機能とは 

    - プロフィール情報へのアクセス権を認可できる人を本人とみなしてログインを許可する 

    機能と言えそう


    - User Social Authを利用する≠OAuthを利用する

    - IdP(Identity Provider)が提供する機構にあわせたバックエンド実装が選択される 

    - GitHubはOAuth2.0 / TwitchはOIDC / Twitterはつい最近までOAuth1.0の独自拡張 

    OAuthについて

    26


    View full-size slide

  27. OAuthのおさらい

    - 今回は認可コード付与(Authorization Code Grant)フローを例にします

    - WebアプリケーションでOAuthを利用する場合のスタンダード 

    - 実際の挙動はIdP毎に独自拡張がされている場合も少なくない 


    - が、発表時間の都合上、大幅にカットしてお伝えします😢

    - エラーの原因となりやすい構造 にフォーカスします


    - 正常系の動作が気になる方は…

    - 資料の末尾に簡単な補足をつけています 

    - Auth屋さんが執筆されている書籍がわかりやすいのでオススメ 

    - https://www.amazon.co.jp/dp/B07XT8H2YG 

    OAuthについて

    27


    View full-size slide

  28. 28

    OAuthのおさらい

    ユーザーの
    Webブラウザ
    バックエンド
    サーバー
    認可
    エンドポイント
    プロフィール取得
    エンドポイント
    トークン
    エンドポイント
    1.

    2-1.

    処理シーケンス

    Code State
    Code State
    Token
    Code
    Token
    State
    Token
    State
    State
    State
    State
    Code
    Session
    →

    State
    Session
    ←

    2-2.

    3-1.

    3-2.

    Secret
    Secret
    💃

    遷移がめっちゃ複雑なので 

    OAuth Danceと呼ばれます 


    今回はポイントを絞って 

    説明します


    View full-size slide

  29. 29

    OAuthのおさらい

    ポイントその1

    ユーザーの
    Webブラウザ
    バックエンド
    サーバー
    認可
    エンドポイント
    プロフィール取得
    エンドポイント
    トークン
    エンドポイント
    Secret
    󰔡

    登場人物が多く、

    数秒の処理の間で

    インターネット間の通信が 

    多く発生します


    View full-size slide

  30. 302 Moved Temporarily

    Location: https://github.com/login/oauth/authorize?state=sss…

    302 Moved Temporarily

    Location:
    http://localhost:8000/social/complete/github
    ?code=ttt…

    30

    OAuthのおさらい

    ポイントその2

    🔄

    HTTPリダイレクトと

    クエリ文字列を駆使して、

    認可に必要な情報を 

    やりとりします

    State
    Code State

    View full-size slide

  31. 31

    OAuthのおさらい

    ポイントその3

    🍪

    セキュリティ上の理由から、 

    セッション≒HTTP Cookie

    に依存する実装がほとんどです 

    ユーザーの
    Webブラウザ
    バックエンド
    サーバー
    2-1.

    Code State
    State
    State
    Session
    →

    State
    3-2.

    Secret
    Session
    ←


    View full-size slide

  32. OAuthのおさらい

    32

    🤖

    わからなくても安心してください

    退屈なことは Python Social Auth がやってくれます

    正 常 系


    View full-size slide

  33. OAuthのおさらい

    33

    🧠

    人間はもっと刺激的なことについて

    考えていきましょう…

    異 常 系


    View full-size slide

  34. 現実世界において生じる異常系処理

    34


    View full-size slide

  35. - 認可処理について言及している?例外クラス

    - AuthFailed … 何らかの理由で認証に失敗した

    - AuthCanceled … ユーザーによって認証がキャンセルされた 

    - AuthUnknownError … 不明なエラーによって認証プロセスが停止した 

    - AuthTokenError … 不正なアクセストークンによって操作がおこなわれた 

    - AuthMissingParameter… 必要なパラメータが不足していた

    - AuthStateMissing … 認可サーバーのレスポンスに state パラメーターがない 

    - AuthStateForbidden … 返却された state パラメーターが送信されたものと異なる 


    - 認可成功後の処理について言及している?例外クラス

    - AuthAlreadyAssociated … アカウントが既に別のユーザーに紐付けられている 

    - NotAllowedToDisconnect … アカウント切断時、ユーザーが他の認証 / 認可SNSを持っていない 

    - AuthTokenRevoked … ユーザーがIdP側で access_token を取り消している 


    - その他の例外クラス

    - WrongBackend … URLに指定されたIdPのエンドポイントが無効 

    - AuthUnreachableProvider … サーバーがIdPのエンドポイントと通信できなかった 

    - SocialAuthBaseException … 上記例外の基底クラス

    現実世界において生じる異常系処理

    どんなエラーが起きるか in Python Social Auth

    35

    引用: https://python-social-auth.readthedocs.io/en/latest/exceptions.html (2022年10月11日閲覧 作成者翻訳) 

    🤔


    View full-size slide

  36. - 認可処理について言及している?例外クラス

    - AuthFailed … 何らかの理由で認証に失敗した

    - AuthCanceled … ユーザーによって認証がキャンセルされた 

    - AuthUnknownError … 不明なエラーによって認証プロセスが停止した 

    - AuthTokenError … 不正なアクセストークンによって操作がおこなわれた 

    - AuthMissingParameter… 必要なパラメータが不足していた

    - AuthStateMissing … 認可サーバーのレスポンスに state パラメーターがない 

    - AuthStateForbidden … 返却された state パラメーターが送信されたものと異なる 


    - 認可成功後の処理について言及している?例外クラス

    - AuthAlreadyAssociated … アカウントが既に別のユーザーに紐付けられている 

    - NotAllowedToDisconnect … アカウント切断時、ユーザーが他の認証 / 認可SNSを持っていない 

    - AuthTokenRevoked … ユーザーがIdP側で access_token を取り消している 


    - その他の例外クラス

    - WrongBackend … URLに指定されたIdPのエンドポイントが無効 

    - AuthUnreachableProvider … サーバーがIdPのエンドポイントと通信できなかった 

    - SocialAuthBaseException … 上記例外の基底クラス

    現実世界において生じる異常系処理

    どんなエラーが起きるか in Python Social Auth

    36

    引用: https://python-social-auth.readthedocs.io/en/latest/exceptions.html (2022年10月11日閲覧 作成者翻訳) 

    どうしてこの分類なのかよくわからない
    システムエラー?ユーザー入力の問題?
    🤔


    View full-size slide

  37. 現実世界において生じる異常系処理

    - RFC6749「The OAuth 2.0 Authorization Framework」において、

    エラーについて説明しているのは主に以下

    - 4. Obtaining Authorization 〜 4.1. Authorization Code Grant 〜 4.1.2. Authorization Response 

    - 要約:認可エンドポイントへの要求失敗時 のエラーレスポンスの 仕様を定めるよ


    - 5. Issuing an Access Token 〜 5.2. Error Response 

    - 要約:トークンエンドポイントへの要求失敗時 のエラーレスポンスの 仕様を定めるよ


    - 7. Accessing Protected Resource 〜 7.2. Error Response 

    - 要約:保護対象リソースへの要求失敗時 も何かしらのエラーを返却すべきだけど、 

    この仕様の範疇外だよ


    - 8. Extensibility 〜 8.5. Defining Additional Error Codes 

    - 要約:他にもエラーコードが必要な場合は 追加で定義してもいいよ 

    - 結局は各IdPが固有に実装したもの を適切に処理する必要がある 

    RFCに立ち返ってみると

    37


    View full-size slide

  38. 38

    現実世界において生じる異常系処理

    ユーザーの
    Webブラウザ
    バックエンド
    サーバー
    認可
    エンドポイント
    プロフィール取得
    エンドポイント
    トークン
    エンドポイント
    1.

    2-1.

    どこでエラーが起きるか

    Code State
    Code State
    Token
    Code
    Token
    State
    Token
    State
    State
    State
    State
    Code
    Session
    →

    State
    Session
    ←

    2-2.

    3-1.

    3-2.

    Secret
    Secret
    A. 認可エンドポイントで 

    画面表示時にエラー発生、 

    バックエンドサーバーに 

    HTTP302でリダイレクト 


    B. 認可エンドポイントで 

    認可操作実行時にエラー発生、 

    バックエンドサーバーに 

    HTTP302でリダイレクト 


    C. トークンエンドポイントで 

    トークン交換時にエラー発生、 

    バックエンドサーバーに 

    HTTP400でレスポンス返却 


    D. (リソース取得でエラー発生) 

    A.
    B.
    C.
    D.

    View full-size slide

  39. 現実世界において生じる異常系処理

    - 認可エンドポイントで発生するエラー(A. B.)

    - invalid_request … リクエストが不正な形式である

    - access_denied … リクエストが拒否された

    - invalid_scope … 要求されたスコープが適切でない

    - unauthorized_client … 付与タイプが許可されていない

    - unsupported_response_type … 付与タイプに対応していない

    - server_error … 予期せぬエラー

    - temporarily_unavailable … 過負荷等でリクエストを処理不能


    - トークンエンドポイントで発生するエラー(C.)

    - invalid_request … リクエストが不正な形式である

    - invalid_grant … 不正な認可コード等で認可できない 

    - invalid_client … クライアントが認証できない

    - invalid_scope … 要求されたスコープが適切でない

    - unauthorized_client … 認可フローが許可されていない

    - unsupported_grant_type … 付与タイプに対応していない


    - リソース取得時に発生するエラー(D.)

    - 仕様上は未定義のため、今回は割愛

    どんなエラーが起きるか in RFC

    39

    HTTP/1.1 302 Found

    Location: http://localhost:8000/cb?error=access_denied&state=xyz

    HTTP/1.1 400 Bad Request

    Content-Type: application/json;charset=UTF-8


    {

    "error":"invalid_request"

    }

    引用: https://openid-foundation-japan.github.io/rfc6749.ja.html (2022年10月11日閲覧) 


    View full-size slide

  40. 現実世界において生じる異常系処理

    - 認可エンドポイントで発生するエラー(A. B.)

    - invalid_request … リクエストが不正な形式である

    - access_denied … リクエストが拒否された

    - invalid_scope … 要求されたスコープが適切でない

    - unauthorized_client … 付与タイプが許可されていない

    - unsupported_response_type … 付与タイプに対応していない

    - server_error … 予期せぬエラー

    - temporarily_unavailable … 過負荷等でリクエストを処理不能


    - トークンエンドポイントで発生するエラー(C.)

    - invalid_request … リクエストが不正な形式である

    - invalid_grant … 不正な認可コード等で認可できない 

    - invalid_client … クライアントが認証できない

    - invalid_scope … 要求されたスコープが適切でない

    - unauthorized_client … 認可フローが許可されていない

    - unsupported_grant_type … 付与タイプに対応していない


    - リソース取得時に発生するエラー(D.)

    - 仕様上は未定義のため、今回は割愛

    どんなエラーが起きるか in RFC

    40

    HTTP/1.1 302 Found

    Location: http://localhost:8000/cb?error=access_denied&state=xyz

    HTTP/1.1 400 Bad Request

    Content-Type: application/json;charset=UTF-8


    {

    "error":"invalid_request"

    }

    引用: https://openid-foundation-japan.github.io/rfc6749.ja.html (2022年10月11日閲覧) 

    ユーザー操作と紐づくものもある
    RFC側で網羅的に定義することを目指してい
    ない(カスタムエラーも可のため)
    基本、リダイレクト時にクエリ文字列
    が欠けるとエラーになる

    View full-size slide

  41. - 認可処理について言及している?例外

    - AuthFailed … 何らかの理由で認証に失敗した

    - AuthCanceled … ユーザーによって認証がキャンセルされた 

    - AuthUnknownError … 不明なエラーによって認証プロセスが停止した 

    - AuthTokenError … 不正なアクセストークンによって操作がおこなわれた 

    - AuthMissingParameter… 必要なパラメータが不足していた

    - AuthStateMissing … 認可サーバーのレスポンスに state パラメーターがない 

    - AuthStateForbidden … 返却された state パラメーターが送信されたものと異なる 


    - 認可成功後の処理について言及している?例外

    - AuthAlreadyAssociated … アカウントが既に別のユーザーに紐付けられている 

    - NotAllowedToDisconnect … アカウント切断時、ユーザーが他の認証 / 認可SNSを持っていない 

    - AuthTokenRevoked … ユーザーがIdP側で access_token を取り消している 


    - その他の例外

    - WrongBackend … URLに指定されたIdPのエンドポイントが無効 

    - AuthUnreachableProvider … サーバーがIdPのエンドポイントと通信できなかった 

    - SocialAuthBaseException … 上記例外の基底クラス

    現実世界において生じる異常系処理

    改めて、どんなエラーが起きるか in Python Social Auth

    41

    引用: https://python-social-auth.readthedocs.io/en/latest/exceptions.html (2022年10月11日閲覧 作成者翻訳) 

    RFCには存在しない観点だったことがわかる
    RFCと対応していそうなものも多い

    View full-size slide

  42. 現実世界において生じる異常系処理

    コードも見てみよう

    42

    class BaseOAuth2(OAuthAuth):
    """Base class for OAuth2 providers.
    OAuth2 details at:
    https://datatracker.ietf.org/doc/html/rfc6749
    """
    # 中略
    def process_error(self, data):
    if data.get('error'):
    if 'denied' in data['error'] or 'cancelled' in data['error']:
    raise AuthCanceled(self, data.get('error_description'
    , ''))
    raise AuthFailed(self, data.get('error_description'
    ) or
    data['error'])
    elif 'denied' in data:
    raise AuthCanceled(self, data['denied'])
    class GithubOAuth2(BaseOAuth2):
    """Github OAuth authentication backend"""
    エラー判定処理
    RFCで規定されるエラーをざっくり例外に束ねる
    https://github.com/python-social-auth/social-core/blob/master/social_core/backends/oauth.py 

    https://github.com/python-social-auth/social-core/blob/master/social_core/backends/github.py 

    GitHub用の認証バックエンド
    共通クラスを継承して動作を共有している

    View full-size slide

  43. 現実世界において生じる異常系処理

    - リダイレクトの過程でクエリ文字列が変わるとエラー

    - ユーザーがメーラー / WebView / Curl等の特殊な環境からアクセスしてきたら? 

    - ユーザーが手動でURLを書き換えたら? 



    - 認可処理の途中でセッション状態が変わるとエラー

    - 認可URLを別のブラウザにコピペされたら? 

    - 複数のタブで並行に認可操作をおこなわれたら? 


    - IdPのエンドポイントが落ちてたらエラー

    - Connection / Read Timeoutなんかもありえる 

    - バックエンド側の実装によっては、 

    urllib.error.HTTPError が投げられることも… 

    ポイントを踏まえたエラーが起きやすい箇所

    43


    View full-size slide

  44. 現実世界において生じる異常系処理

    44

    💡

    勘所がつかめてきたところで、

    解決へ向けて考えていきましょう


    View full-size slide

  45. 現実世界において生じる異常系処理

    - 4種類に分けて整理してみます

    分類を試みる

    45

    エラーの種類 例外の一例 解消方法 監視レベル
    a. ユーザの操作に
    起因する認可エラー
    AuthCanceled
    AuthTokenRevoked
    ??? ???
    b. ユーザーの環境に
    起因する認可エラー
    AuthMissingParameter
    AuthStateForbidden
    ??? ???
    c. IdPに起因する
    認可エラー
    AuthUnreachableProvider
    HTTPError
    ??? ???
    d. その他のエラー AuthAlreadyAssociated
    WrongBackend
    ??? ???

    View full-size slide

  46. 現実世界において生じる異常系処理

    - 認可フロー中でユーザーが明示的におこなった操作により発生したエラー

    - 認可画面で拒否をおこなった 

    - 一度認可したが後日取り消しをおこなったあと、再度認可フローに入った 


    - 解決するには、もう一度最初から認可フローをおこなってもらう

    - もしもユーザーが誤って認可拒否をおこなっていたのであれば再操作で解決 

    - ユーザーが意識的に拒否をおこなっていたのであれば、そもそもエラーではない 


    - エラー監視は不要 or 発生件数のトレンドがわかれば十分

    - ユーザー操作に起因するため、0件になることはない 

    - 監視ツール(例: Sentry)に通知した上でIgnore or エラー通知しない 

    - OSS側のバグが不安なら発生数を追えるようにしてもよいかも 

    a. ユーザの操作に起因する認可エラー

    46


    View full-size slide

  47. 現実世界において生じる異常系処理

    a. に対するユーザーの体験イメージ

    47

    ℹ 認証がキャンセルされました。
    ログインを希望する場合は、再度操作
    をおこなってください。
    何も表示しなくても
    十分かも?

    View full-size slide

  48. 現実世界において生じる異常系処理

    - ユーザーの環境(Webブラウザ)に起因して発生したエラー

    - フロー中に何かしらの事情でクエリパラメータ / Cookie(≒セッション)が失われた 


    - 解決するには、もう一度最初から認可フローをおこなってもらう(再)

    - プログラム側のバグではないため再操作してもらう他ない 

    - 正常な認可フローを阻害しうる要因について設定を確認してもらうお願いする 

    - シークレットブラウザやCookieの設定 

    - 一連の操作を同一のWebブラウザで操作しているかどうか 


    - エラー監視は発生件数のトレンドがわかれば十分

    - a. よりは重要度は高いが、ブラウザに起因する以上0件にはなりえない 

    - 一時的 or あるタイミング以降大量発生している…といったことには気づけた方がよい 

    b. ユーザーの環境に起因する認可エラー

    48


    View full-size slide

  49. 現実世界において生じる異常系処理

    b. に対するユーザーの体験イメージ

    49

    🚫認証に失敗しました。
    以下を確認し、再度操作をおこなってく
    ださい。
    ・Cookieが有効されていること
    ・対応ブラウザでサイトを閲覧しているこ

    View full-size slide

  50. 現実世界において生じる異常系処理

    - IdP(認可サーバー)の状態に起因して発生したエラー

    - 一時的に高負荷になっている 


    - 解決するには、もう一度最初から認可フローをおこなってもらう(再々)

    - 自分たちでコントロールできる範囲でないためもう一度操作してもらうほかない 

    - 一方で、ユーザー体験の毀損度合いは高い ので相応なメッセージを出したほうがよさそう 

    - 外部サービスのエラーであること 

    - 解消まで待ってもらうしかないこと 

    - 他の認証方法でログイン可能ならそれを試してもらうこと 


    - エラー監視は急激な発生を検知できたほうがよい

    - 時間が長引くようなら障害報を出したほうがいいケースも 

    - HTTPリダイレクト先でエラーになる可能性も高いので検知方法は要検討 

    c. IdPに起因する認可エラー

    50


    View full-size slide

  51. 現実世界において生じる異常系処理

    c. に対するユーザーの体験イメージ

    51

    🚫連携サービスの一時的な不具合により
    認証ができない状況になっています。
    しばらく待ってから再度操作するか、他の
    ログイン方法をお試しください。
    こういう体験になる可能性も高いので、
    ログインページにFAQへの導線があってもいいかも

    View full-size slide

  52. - a.~c.以外で発生していて、OAuthに関連すると思われるエラー

    - エラーは多岐に渡るため、網羅的に考えるのがベター 


    - 解決するには、プログラムの修正が必要な可能性が高い

    - 例えばAuthAlreadyAssociated であれば、アプリケーションの要件にあわせて仕様検討が必要 

    - ひとつのアカウントに複数SNS認証を実施できる場合は要注意かも 

    - WrongBackend であれば、OAuthのアプリケーション設定を壊してしまった可能性がある 


    - エラー監視は注視する必要あり

    - よくわからないものを d. のエラーとしてまず捉え、a.~c.に分類できるか考える 

    - 分類できそうならエラーメッセージを返却するようにして監視レベルを下げる 

    - 分類できなさそうならバグとして起票 & 対処 

    現実世界において生じる異常系処理

    d. その他のエラー

    52


    View full-size slide

  53. Python Social Authを使ったソーシャルログイン機能の実装

    d. に対するユーザーの体験イメージ

    53

    🚫認証に失敗しました。
    恐れ入りますが、しばらくしてから再度操
    作をおこなってください。
    とりあえずリトライを促すメッセージは
    出しておき、エラー発生したら検知 &
    エンジニアで調査する

    View full-size slide

  54. まとめ

    54


    View full-size slide

  55. まとめ

    ソーシャルログインはUXを高める素晴らしい機能です😍

    55


    View full-size slide

  56. 56

    まとめ

    ユーザーの
    Webブラウザ
    バックエンド
    サーバー
    認可
    エンドポイント
    プロフィール取得
    エンドポイント
    トークン
    エンドポイント
    1.

    2-1.

    OAuthはとても複雑💃

    Code State
    Code State
    Token
    Code
    Token
    State
    Token
    State
    State
    State
    State
    Code
    Session
    →

    State
    Session
    ←

    2-2.

    3-1.

    3-2.

    Secret
    Secret
    🤖

     正常系はライブラリに任せて、 

    🧠

    異常系に注力しましょう 


    View full-size slide

  57. まとめ

    - まず d. を疑い、a. ~ c.に分類する or 不具合解消を目指しましょう

    異常系処理の乗りこなし方🐎

    57

    エラーの種類 一例 解消方法 監視レベル
    a. ユーザの操作に
    起因する認可エラー
    ・ユーザが認可を拒否した
    ・認可後に許可を取り消した
    再操作
    してもらう
    不要or発生数
    b. ユーザーの環境に
    起因する認可エラー
    ・クエリパラメータが消えた
    ・セッションが消えた
    再操作
    してもらう
    発生数
    c. IdPに起因する
    認可エラー
    ・認可サーバーが落ちてる 再操作
    してもらう
    急激な増加
    発生数
    d. その他のエラー ・まだよくわからないエラー エラーにより
    異なる
    要対応

    View full-size slide

  58. 参考文献

    58


    View full-size slide

  59. - Justin Richer, Antonio Sanso.『OAuth徹底入門 セキュアな認可システムを適用するための原則と実践』. 翔泳社, 2019, p464


    - Auth屋.『雰囲気で使わずきちんと理解する!整理してOAuth2.0を使うためのチュートリアルガイド』. インプレスR&D, 2019, p94


    - Peter Yaworski.『リアルワールドバグハンティング』. オーム社, 2020, p281


    - 芝田 将.『実践Django Pythonによる本格Webアプリケーション開発』. 翔泳社, 2021, p312

    59

    参考文献


    View full-size slide

  60. - D. Hardt, Ed.「The OAuth 2.0 Authorization Framework」. https://www.ietf.org/rfc/rfc6749.html, (2022年10月11日閲覧)


    - OpenID Foundation Japan. 「The OAuth 2.0 Authorization Framework」.
    https://openid-foundation-japan.github.io/rfc6749.ja.html, (2022年10月11日閲覧)


    - Aaron Paracki.「OAuth Community Site」. https://oauth.net/, (2022年10月11日閲覧).


    - Python Social Auth.「Welcome to Python Social Auth’s documentation!」.

    https://python-social-auth.readthedocs.io/en/latest/, (2022年10月11日閲覧).


    - GitHub Docs.「Building OAuth Apps」.

    https://docs.github.com/en/developers/apps/building-oauth-apps, (2022年10月11日閲覧).

    60

    参考文献


    View full-size slide

  61. 補足資料

    61


    View full-size slide

  62. OAuth認可コード付与フローの概要

    - リソース所有者(Resource Owner)

    - 私たちが構築したWebアプリケーションの利用者 

    - プロフィールを所有しており、IdPに情報の登録をおこなっている 


    - クライアント(Client)

    - 私たちが構築したWebアプリケーションのバックエンド サーバー

    - 利用者のプロフィール情報を取得したいと思っている 


    - 認可サーバー(Authorization Server)

    - IdPが構築したOAuthのエンドポイント 

    - 認可エンドポイントとトークンエンドポイント を持つ


    - 保護対象リソース(Protected Resource)

    - IdPが構築したプロフィール取得のエンドポイント 

    - 認可されたクライアントに対して情報を提供する 

    認可コード付与フローにおける登場人物

    62


    View full-size slide

  63. 63

    OAuth認可コード付与フローの概要

    ● URLへのアクセスに対してログインページを
    返却

    1. ログインページ表示

    ユーザーの
    Webブラウザ
    バックエンド
    サーバー
    1.

    GET http://localhost:8000/accounts/ 

    200 OK


    View full-size slide

  64. 64

    OAuth認可コード付与フローの概要

    ● ログインボタンの押下に対してSNS認可に必
    要な情報をクエリ文字列に含むURLを 

    構築(state, scope, client_id, etc...) 


    ● GitHubの認可画面にリダイレクト 

    2.SNS認可画面へ遷移

    ユーザーの
    Webブラウザ
    バックエンド
    サーバー
    認可
    エンドポイント
    2-1.

    State
    State
    State
    Session
    →

    GET http://localhost:8000/social/login/github 

    302 Moved Temporarily 

    Location: https://github.com/login/oauth/authorize?state=sss… 

    GET https://github.com/login/oauth/authorize?state=sss… 

    State
    200 OK

    2-2.

    👶

    Secret

    View full-size slide

  65. 65

    OAuth認可コード付与フローの概要

    ユーザーの
    Webブラウザ
    認可
    エンドポイント
    ● ユーザーが許可ボタンを押すと 

    認可エンドポイントより 認可コード(code)が発行さ
    れる

    ○ 1回限りアクセストークンに交換できる値 


    ● 認可コードをクエリ文字列に含めた上で、 

    元いたWebサイトにリダイレクト 

    3.プロフィール情報取得

    3-1.

    3-2.

    State
    Code
    State Code
    POST https://github.com/login/oauth/authorize 

    302 Moved Temporarily 

    Location: http://localhost:8000/social/complete/github?code=ttt… 

    👶

    (次ページへ)


    View full-size slide

  66. 66

    OAuth認可コード付与フローの概要

    ユーザーの
    Webブラウザ
    バックエンド
    サーバー
    プロフィール取得
    エンドポイント
    トークン
    エンドポイント
    ● 認可コード&Stateがバックエンド
    サーバーに渡される 


    ● トークンエンドポイントを使い 

    認可トークンをアクセストークン
    (access_token)に交換


    ● トークンを付与してプロフィール取
    得エンドポイントへ


    ● プロフィール情報を元にアカウン
    ト作成やログインを実施 

    3-2.

    State Code
    State
    Session
    ←

    Token
    Token
    GET http://localhost:8000/social/complete/github?code=ttt&state=sss… 

    POST https://github.com/login/oauth/access_token 

    Code
    200 OK

    200 OK

    200 OK

    👶

    Secret
    Secret
    Token
    GET https://api.github.com/user 

    3.プロフィール情報取得


    View full-size slide

  67. 補足資料

    Middlewareの実装イメージ

    67

    class CustomizedSocialAuthExceptionMiddleware(SocialAuthExceptionMiddleware):
    def get_message (self, request, exception):
    if isinstance (exception , AuthCanceled):
    # a. ユーザの操作に起因する認可エラー
    return "ℹ認証がキャンセルされました。ログインを希望する場合は、再度操作をおこなってください。 "
    elif isinstance (exception , AuthMissingParameter):
    # b. ユーザーの環境に起因する認可エラー
    # エラー発生を監視システムに通知する(その上で無視する)
    self.capture_exception(exception)
    return "🚫認証に失敗しました。以下を確認し、再度操作をおこなってください。〜 "
    elif isinstance (exception , HTTPError):
    # c. IdP に起因する認可エラー
    # エラー発生を監視システムに通知する(短時間で一定量発生しない限り無視する)
    self.capture_exception(exception)
    return (
    "🚫連携サービスの一時的な不具合により認証ができない状況になっています。〜 "
    )
    else:
    # d. その他のエラー
    # エラー発生を監視システムに通知し、 a. ~ c. のエラーに振り分けてよいか検討する
    self.capture_exception(exception)
    return "🚫認証に失敗しました。恐れ入りますが、しばらくしてから再度操作をおこなってください。 "
    def capture_exception (self, exception):
    # Sentry などのエラー監視システムにエラー情報を送信
    pass
    (こぼれ話)デフォルトだと
    AuthExceptionを継承しない例外は
    Middlewareにやってこないので、
    process_exception を override して
    例外発生したrequest.pathや
    exceptionの内容をみてハンドリング
    をおこなう必要があるので注意

    View full-size slide