はじめに
締切に追われる完璧主義者向けのWebアプリケーションフレームワークDjangoは、ユーザの認証機能もバッチリ備えています。
例えばDjango Girls Tutorialでは、少しのコード量でログインを実装しています。
https://tutorial-extensions.djangogirls.org/ja/authentication_authorization/#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3
Djangoの認証機能を使って全然よいのですが、ここをAuth0にお任せできるということを知り、素振りしました。
目次
Auth0
Auth0はWebアプリやモバイル、APIなどに対して認証・認可のサービスをクラウドで提供している、いわゆるIDaaS (Identity as a Service)ベンダーです。
フューチャー技術ブログに導入編がありました。
Djangoでの事例はこちら。
hokanさんの例です
DjangoでAuth0のQuickstart
以下のチュートリアルで素振りしていきます。
チュートリアルの完成コードはこちら
auth0-django-web-app/01-Login at master · auth0-samples/auth0-django-web-app · GitHub
Auth0で認証するDjangoアプリ
ユーザは http://127.0.0.1:3000/ にアクセス2
(画像はポート番号を間違えて撮影してしまっています)
「Login」は http://127.0.0.1:3000/accounts/login へのリンクになっています。
今回の実装では、「Login」をクリックした後、Auth0側に遷移します。
Auth0で認証した後は、Djangoアプリの http://127.0.0.1:3000/accounts/callback (callback URL) にリダイレクトされます。
その後、ユーザはDjangoアプリにログイン扱いとなり、ログインユーザ用の表示がされます
(Auth0から返ってきているトークンが表示される実装です。開発者向けチュートリアルのためと思います)
動作環境
Python 3.11.8
authlib django django-environ requests
requestsは私たちのDjangoアプリではimport
しないのですが、authlibが使っています
具体的なバージョンはこちらをどうぞ(Django 5.0で動いています)
https://github.com/ftnext/django-auth0-practice/blob/0947c946e96feb872e1dc32be4a9600996d347a1/requirements.txt
Auth0の設定
Auth0に開発者用アカウントを作ると「Default App」というApplicationが作成されていました。
今回の素振りではこれを使っています
(1) Djangoアプリの環境変数に設定する値たち
(django-environで.envから読み取るように実装しました3)
「Basic Information」の
- Domain
- Client ID
- Client Secret
(2) Djangoアプリに関連する値を設定する箇所
「Application URIs」の
- Allowed Callback URLsに
- Allowed Logout URLsに
ほかの箇所はいじっていません
DjangoアプリでAuth0を使う実装
チュートリアルに沿ってビューに実装しています。
チュートリアルからアプリケーションの切り分け方を変えて、accounts
アプリを導入しました。
authlibライブラリを使っていきます
# accounts/views.py from authlib.integrations.django_client import OAuth from django.conf import settings oauth = OAuth() oauth.register( "auth0", client_id=settings.AUTH0_CLIENT_ID, client_secret=settings.AUTH0_CLIENT_SECRET, client_kwargs={"scope": "openid profile email"}, server_metadata_url=f"https://{settings.AUTH0_DOMAIN}/.well-known/openid-configuration", )
ログイン
https://docs.authlib.org/en/latest/client/django.html#routes-for-authorization
def login(request): return oauth.auth0.authorize_redirect( request, request.build_absolute_uri(reverse("accounts:callback")) )
oauthオブジェクトはAuth0を使うように設定されており、Djangoアプリへのログインではauthorize_redirect
を呼び出しています。
これでAuth0側に遷移するという理解です
コールバック
Auth0で認証されたら、Djangoアプリ側のcallback URLにリダイレクトされます(ここでリダイレクト先は、「Allowed Callback URLs」になければならない)
def callback(request): token = oauth.auth0.authorize_access_token(request) request.session["user"] = token return redirect(request.build_absolute_uri(reverse("myapp:index")))
トークンを取得し、セッション(request.session
)4に保存しています5。
その後、Djangoアプリのindexページにリダイレクトし、ログインしているのでユーザの情報が表示されます。
indexページ(ログインしたときの見え方)
myappアプリケーションに実装しました。
# myapp/views.py def index(request): return render( request, "myapp/index.html", context={ "session": request.session.get("user"), "pretty": json.dumps(request.session.get("user"), indent=4), }, )
テンプレートでは{% if session %}
と分岐しています
- Auth0で認証されると、callback URLで
request.session["user"]
を設定するので、{% if session %}
はTrue
の分岐へ。ログインユーザ向けの表示ができる - Auth0で認証する前やログアウトした後は
request.session
にuserというキーが設定されていないので、{% if session %}
はFalse
の分岐へ。「Welcome Guest」表示
ログアウト
# accounts/views.py def logout(request): request.session.clear() return redirect( f"https://{settings.AUTH0_DOMAIN}/v2/logout?" + urlencode( { "returnTo": request.build_absolute_uri(reverse("myapp:index")), "client_id": settings.AUTH0_CLIENT_ID, }, quote_via=quote_plus, ) )
セッションをクリアしています。
その後Auth0側にリダイレクトし、Auth0でもログアウトさせています。
ログイン状態でなければ表示しないページはどう作る?
Auth0を使った場合、@login_required
に相当するビューはどのように実装するのでしょうか。
I wrote a small function that gets email from request.session.get(“user”), and then find the user.
@login_required
はuser.is_authenticated
を見ています6が、この属性はAuth0で認証済みか否かを意味しません
indexページの実装も参考に、以下のように実装しました(ダッシュボード画面を追加)。
実務で使う場合はもっと突き詰めたいと考えています(Auth0のサンプルコードの中にあるかな?)
# myapp/views.py def dashboard(request): if not request.session.get("user"): return redirect_to_login( request.build_absolute_uri(), reverse("accounts:login") ) return render(request, "myapp/dashboard.html")
- ログアウトした状態で http://127.0.0.1:3000/dashboard/ にアクセス
- Djangoアプリはloginページにリダイレクト。ユーザにはAuth0の認証が見える
- Auth0で認証した後は http://127.0.0.1:3000/dashboard/ へ
- nextパラメータを使って、ログイン状態でダッシュボード画面を表示できないのかな?(宿題事項)
- Auth0で認証後 http://127.0.0.1:3000/dashboard/ も見える
終わりに
Auth0で認証するDjangoアプリの素振りの記でした。
こんなことができるんですね〜
hokanさんの事例にあるのですが、認証をAuth0に切り出すことで「二要素認証やAD連携など機能強化」にもつなげられるそうです。
私の関わる開発で使い所があるかは分かりませんが、手札が増えました!
- ゼロと言ったら ↩
-
python manage.py runserver 3000
↩ - .envを用意し(テンプレート)、settings.pyで参照しています↩
- 「読み書き可能でディクショナリライクなオブジェクト」https://docs.djangoproject.com/ja/5.0/ref/request-response/#django.http.HttpRequest.session↩
- https://docs.authlib.org/en/latest/client/django.html#django-openid-connect-client↩
- https://github.com/django/django/blob/5.0.6/django/contrib/auth/decorators.py#L51↩