概要
OIDCで認証した後、ロジック上の任意の箇所でアクセストークンを取得する方法を調べた。
やり方
まずはsettings.pyにSOCIALACCOUNT_STORE_TOKENSという設定値をTrueに設定する。 ※ デフォルトはFalse
settings.py
# ...省略 SOCIALACCOUNT_STORE_TOKENS = True
https://docs.allauth.org/en/dev/socialaccount/configuration.html
この状態で以下の記事のようにOIDC認証する。
Django Adminで確認するとSocial application tokensというモデルにデータが登録されている。

Django Admin上の表示名と微妙に異なるのでわかりにくいが、モデル上はSocialTokenという名前である。
class SocialToken(models.Model): app = models.ForeignKey(SocialApp, on_delete=models.SET_NULL, blank=True, null=True) account = models.ForeignKey(SocialAccount, on_delete=models.CASCADE) token = models.TextField( verbose_name=_("token"), help_text=_('"oauth_token" (OAuth1) or access token (OAuth2)'), ) token_secret = models.TextField( blank=True, verbose_name=_("token secret"), help_text=_('"oauth_token_secret" (OAuth1) or refresh token (OAuth2)'), ) expires_at = models.DateTimeField( blank=True, null=True, verbose_name=_("expires at") ) class Meta: unique_together = ("app", "account") verbose_name = _("social application token") verbose_name_plural = _("social application tokens") def __str__(self): return "%s (%s)" % (self._meta.verbose_name, self.pk)
https://github.com/pennersr/django-allauth/blob/main/allauth/socialaccount/models.py
Django Adminは以下のように実装されていて、画面上からは完全なトークンを確認できない。
class SocialTokenAdmin(admin.ModelAdmin): raw_id_fields = ( "app", "account", ) list_display = ("app", "account", "truncated_token", "expires_at") list_filter = ("app", "app__provider", "expires_at") def truncated_token(self, token): max_chars = 40 ret = token.token if len(ret) > max_chars: ret = ret[0:max_chars] + "...(truncated)" return ret truncated_token.short_description = "Token" # type: ignore[attr-defined]
https://github.com/pennersr/django-allauth/blob/main/allauth/socialaccount/admin.py
従って、以下のようにDjango Shellからアクセストークンを取得してみる。
>>> from allauth.socialaccount.models import SocialToken >>> from app.models import AppUser >>> user = AppUser.objects.get(username="test") >>> from allauth.socialaccount.models import SocialAccount >>> social_accoount = SocialAccount.objects.get(user=user, provider="keycloak") >>> token = SocialToken.objects.get(account=social_accoount) >>> token.token 'eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIybHY4NjhyR09welhUcmhKRktWOHdxQWwzbWlYYkRpWGRiT2RuVERQWGVrIn0.eyJleHAiOjE3Mzc0Njc4MDYsImlhdCI6MTczNzQ2Nzc0NiwiYXV0aF90aW1lIjoxNzM3NDY3NzQ2LCJqdGkiOiI2N2M2YWY3OS1lZDI4LTQ1NzAtYmNhNi1jMzU0ZDMyYjdjYjAiLCJpc3MiOiJodHRwOi8vcHJveHkvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJiZGIyNzBlOS1kMDc1LTQwODAtYjJkNy02ZDNhNDAzYjg4ZWUiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0ZXN0Y2xpZW50Iiwic2lkIjoiODM5NzA3MGUtODBhMy00ZGM2LTlkN2UtNWVkMjEwYTg5ZDQ1IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vMTI3LjAuMC4xOjE4MDAwIiwiaHR0cDovL2xvY2FsaG9zdDoxODAwMCJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1tYXN0ZXIiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6IuWkqumDjiDlsbHnlLAiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0IiwiZ2l2ZW5fbmFtZSI6IuWkqumDjiIsImZhbWlseV9uYW1lIjoi5bGx55SwIiwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIn0.bxicybELG-Ss0avBiB3cMF7o9CMEvImPd8nvGCqoyukvBbwk3JnWQami8awcKAqf1ckDmHV7ugx2omOpcSN7bESRXxENA6PiVFHtznWEYDQJ3YKQxdjoVdmjKCY3fGbLqLlco1bKXxqWnYhWRCA_TjLnNxVsc2n9paUnz7Nk5_s5K7eOJEBULI1-pw8YSe22x1azaApVe-wjzf-QRvkECmxgv9uLSomf6LxUqXid9HRsrQzGjeJrtbmKiDfzXVmwZc95RbGquk1_uQE3blmGRk5BYbzzTkN-UmoyU2vb2DyfU2F0eeyVrqzFSWALnMolpTU5z1930nzQft50XKyNWw' >>>
取得したアクセストークンが使えるのかを検証する。
Keycloakの場合、エンドポイント一覧は以下のように管理画面から画面からアクセスできる。


あとは以下のようにcurlでUserinfoエンドポイントを叩く。
curl -X GET "http://127.0.0.1/realms/master/protocol/openid-connect/userinfo" -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIybHY4NjhyR09welhUcmhKRktWOHdxQWwzbWlYYkRpWGRiT2RuVERQWGVrIn0.eyJleHAiOjE3Mzc0NzExOTgsImlhdCI6MTczNzQ3MTEzOCwiYXV0aF90aW1lIjoxNzM3NDcwNjAxLCJqdGkiOiIxMzA2ZGUyYi0yZWUxLTQ1OWItOWI0NS01MzU4MjdiMjRjZWIiLCJpc3MiOiJodHRwOi8vcHJveHkvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJiZGIyNzBlOS1kMDc1LTQwODAtYjJkNy02ZDNhNDAzYjg4ZWUiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0ZXN0Y2xpZW50Iiwic2lkIjoiMGExODM4MzktOTY0MC00Nzk0LThiYzQtOWVmZjgwN2E3MzI4IiwiYWNyIjoiMCIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vMTI3LjAuMC4xOjE4MDAwIiwiaHR0cDovL2xvY2FsaG9zdDoxODAwMCJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1tYXN0ZXIiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6IuWkqumDjiDlsbHnlLAiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0IiwiZ2l2ZW5fbmFtZSI6IuWkqumDjiIsImZhbWlseV9uYW1lIjoi5bGx55SwIiwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIn0.Wz2YoZvbS-k6iGJN0VmpkoNaGvmFZ3iTQzKCmPZYo7ac7zNPbbRSsFOff-3zRcaFNr4TvWQpoZ_vD5hhWdslq_GWgCigeBIXPngSgTcQ1d49WmBfua9U9utTGs0dnXqU4sxgdGsaT-uBCJaUEQoY7k7dyKh5rP4X9ld0y9FBA80ZerQ5TR6ow5-keLtNOdczMXNJXsYr806Sm8RKSJyWE-wTGmbFhnVr3DoS_6uXIm2jgthVIiGuyAckRiL4oBQUnYh_PDwtkaBaxB3R0AX3U8gwHpOQYHZBh_CMIounv3LkzZsTQFlANe5fxMvMZeHQ8CCPQZRn6Jlw2XJ42E1sdQ"
これでうまくいくかと思ったが、実際は結果が空になってしまった。
調査はまた次回とする。
→ 調査結果は以下