DjangoBrothers BLOG ✍️

2021/01/06

このエントリーをはてなブックマークに追加
Django ログイン LINE API social-auth-app-djano SNS認証

DjangoでLINEログインを実装 ユーザー名とアイコンの設定方法まで

social-auth-app-djangoを使うと、Google、Twitter、GitHub、Facebookなど様々なプロバイダを使って認証機能を作ることができます。

この記事ではLINEアカウントでの認証について書きます。 social-auth-app-djangoでは、ウェブアプリにLINEログインを組み込むのフローに則って認証機能を提供してくれます。

また、LINEのアカウント名プロフィール画像をUserモデルに設定するサンプルコードも記載します。 Userモデルはデフォルトのものではなく、カスタムユーザーモデルを使っている前提とします。

バージョン

  • Django 3.1.5
  • Python 3.8.4
  • social-auth-app-django 4.0.0

初期設定

ドキュメントに沿ってDjangoに必要な設定を加えます。

まずはライブラリをインストールします。

ライブラリのインストール

$ pip install social-auth-app-django==4.0.0

INSTALLED_APPSにsocial_djangoを追加します。今回は使いませんがTemplateのProcessorsも追加しておきます。

settings.py

INSTALLED_APPS = [
  'django.contrib.admin',
  'django.contrib.auth',
  'django.contrib.contenttypes',
  'django.contrib.sessions',
  'django.contrib.messages',
  'django.contrib.staticfiles',
  'users',
  'social_django',  # 追加
]

TEMPLATES = [
  {
    "BACKEND": "django.template.backends.django.DjangoTemplates",
    "DIRS": [os.path.join(BASE_DIR, "templates")],
    "APP_DIRS": True,
    "OPTIONS": {
      "context_processors": [
        "django.template.context_processors.debug",
        "django.template.context_processors.request",
        "django.contrib.auth.context_processors.auth",
        "django.contrib.messages.context_processors.messages",
        # 追加
        "social_django.context_processors.backends",
        "social_django.context_processors.login_redirect",
      ],
    },
  },
]

マイグレートします。

マイグレート

$ python manage.py migrate

settings.pyにAUTHENTICATION_BACKENDSを追加します。これには認証に使いたいプロバイダを指定します。今回はLINEでの認証を行いたいので以下のようにします。

settings.py

AUTHENTICATION_BACKENDS = (
  'social_core.backends.line.LineOAuth2',  # LINE認証用
  'django.contrib.auth.backends.ModelBackend',
)

# 追加
LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/"

SNS認証とは別に、ユーザー名とパスワードを使った認証もしたい場合は'django.contrib.auth.backends.ModelBackend'も必要です。

ちなみにLINEの他にどのようなプロバイダが使えるかはこちらで確認することができます。

LOGIN_REDIRECT_URL、LOGOUT_REDIRECT_URLは、ログイン時とログアウト時に表示するページを設定します。今回はどちらもトップページに設定してます。

URLの設定をします。

urls.py

from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('social_django.urls', namespace='social')),  # 追加
    path("logout/", auth_views.LogoutView.as_view(), name="logout"),  # 追加
]

続いてテンプレート側にログイン用リンクを表示します。

index.html

<h1>トップページ</h1>

{% if user.is_authenticated %}
    <p>ユーザー名: {{ user.username }}</p>
    <a href="{% url 'logout' %}">ログアウト</a>
{% else %}
    <a href="{% url 'social:begin' 'line' %}">LINEでログインする</a>
{% endif %}

ログイン用リンクはhref="{% url 'social:begin' 'プロバイダ名' %}"の形で指定します。

ここまででリンクの設定は完了です。「LINEでログインする」ボタンを押すと以下のように「400 Bad Request」が表示されているはずです。

LINEログインチャネル作成

ここからは、LINE側での設定をします。

ドキュメント: ウェブアプリにLINEログインを組み込む

まずはチャネルというものを作ります。

LINE Developersコンソールにアクセスして作成します。

チャネルの種類は「LINEログイン」を選択します。プロバイダー名やチャネル名は適当に入力してください。 アプリタイプは「ウェブアプリ」を選択します。

チャネルが作成できると、チャネルIDチャネルシークレットが画面に表示されるので、これらをコピーしておきます。

コピーした値をsettings.pyに追記します。ドキュメントが充実していませんが一応LINEについての説明はこちらのページにあります。

settings.py

SOCIAL_AUTH_LINE_KEY = 'チャネルIDの値'
SOCIAL_AUTH_LINE_SECRET = 'チャネルシークレットの値'

それぞれの値は環境変数から拾う設定にしておくと良いでしょう。

最後に、コールバックURLを設定します。「LINEログイン設定」の画面で、http://127.0.0.1:8000/complete/line/を登録します。

設定はこれで完了です。「LINEでログインする」ボタンからログインできます。

プロバイダ名やチャネル名はこのタイミングでユーザーに表示されます。

無事ログインができると、画面は以下のように切り替わります。usernameフィールドにはデフォルトでUserIDが保存されていることがわかります。

Admin画面の「User social auths」や「Users」のセクションでアカウントデータが追加されていることが確認できます。ちなみに、SNS認証で作成されたアカウントにはパスワードは設定されません

ユーザー名とプロフィール画像の取得

現在はusernameフィールドにUserIDが保存されてしまっていますが、LINEで使っているユーザー名を保存するようにしてみます。 また、プロフィール画像もLINEから取得します。

今回使用するUserモデルは以下のような想定です。

models.py

class User(AbstractBaseUser, PermissionsMixin):
    """ユーザーモデル"""

  username_validator = UnicodeUsernameValidator()

  username = models.CharField(
      _("username"),
      max_length=150,
      unique=True,
      help_text=_("Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."),
      validators=[username_validator],
      error_messages={
          "unique": _("A user with that username already exists."),
      },
  )
  email = models.EmailField("メールアドレス", blank=True)
  sns_icon_url = models.URLField("アイコン(SNS連携)", blank=True)
  is_staff = models.BooleanField("is_staff", default=False)
  is_active = models.BooleanField("is_active", default=True)
  date_joined = models.DateTimeField("date_joined", default=timezone.now)

  objects = UserManager()

  USERNAME_FIELD = "username"
  EMAIL_FIELD = "username"
  REQUIRED_FIELDS = ["email"]
  DEFAULT_ICON_PATH = "images/default_icon.png"

SNSログインした時に、usernameフィールド、sns_icon_urlフィールドにプロバイダから取得したユーザー名とアイコン画像URLを保存するようにします。

実装するためにはPipelineという機能を使います。Pipelineのドキュメントはこちら

settings.pyにSOCIAL_AUTH_PIPELINEを追加します。

settings.py

SOCIAL_AUTH_PIPELINE = (
  "social_core.pipeline.social_auth.social_details",
  "social_core.pipeline.social_auth.social_uid",
  "social_core.pipeline.social_auth.social_user",
  "social_core.pipeline.user.get_username",
  "social_core.pipeline.social_auth.associate_by_email",
  "social_core.pipeline.user.create_user",
  "social_core.pipeline.social_auth.associate_user",
  "social_core.pipeline.social_auth.load_extra_data",
  "social_core.pipeline.user.user_details",
  # ↑ デフォルトの設定

  "users.pipeline.set_user_data",  # users/pipeline.pyのset_user_data関数
)

デフォルトのPipelineに加えて"users.pipeline.set_user_data"を追加します。 ここには、認証時に実行したい関数へのパスを書きます。今回の場合、usersアプリ内のpipeline.pyに定義するset_user_data関数を指定します。自身のプロジェクトに応じて適切に設定してください。

set_user_data関数を作ります。

/users/pipeline.py

def set_user_data(backend, strategy, details, response, user=None, *args, **kwargs):
  """
  SNS認証時にプロバイダから取得したデータをプロフィールに設定する
  """
  if backend.name == "line":
    # ユーザー名を取得。取得できなかった場合はuserIdをユーザー名とする。
    username = response.get("displayName", response["userId"])
    # アイコン未設定の場合は空文字を設定する(1度アイコン設定後にLINE側でアイコンが削除されている可能性もあるので、再度ログインした場合は当サービスからも画像を削除する)
    icon_url = response.get("pictureUrl", "")

  user.username = username
  user.sns_icon_url = icon_url
  user.save()

LINE認証された時、social-auth-app-djangoはユーザープロフィールを取得するAPI(https://api.line.me/v2/profile)を叩くのでこのAPIのレスポンス内容を使うことができます。 レスポンスはresponse引数に代入されているので、response.get("pictureUrl", "")のようにすることで画像URLを取得できたりします。

LINE認証時に何が行われているかは、ライブラリのソースコードsocial_core/backends/line.pysocial_core/backends/oauth.pyなどを参照すると何となく理解できます。

これでLINEから取得したユーザー名とプロフィール画像のURLを保存できます。LINEでアイコンを設定していないアカウントの場合は画像URLは取得されないのでsns_icon_urlフィールドには空文字が保存されます。

以下のようなメソッドを実装しておくと、LINEの画像が設定されているかどうかに応じて、ユーザーのアイコンを切り替えることができます。

models.py

from django.contrib.staticfiles.storage import staticfiles_storage


class User(AbstractBaseUser, PermissionsMixin):
  """ユーザーモデル"""

  ・・・

  @property
  def icon_url(self):
    """ユーザーのアイコン画像URLを返す
    1. SNS認証時にプロフィール画像を取得できた場合: sns_icon_url
    2. SNS認証時にプロフィール画像を取得できなかった場合: デフォルト画像
    """
    if self.sns_icon_url:
      return self.sns_icon_url
    return staticfiles_storage.url("images/default_icon.png")

sns_icon_urlフィールドに値がある場合はそのURLを、ない場合はstaticディレクトリの("images/default_icon.png")に保存しているデフォルトのアイコン画像を返すメソッドです。

テンプレート側では以下のようにするとアイコン画像を表示できます。

index.html

<h1>トップページ</h1>

{% if user.is_authenticated %}
  <p>ユーザー名: {{ user.username }}</p>
  <img src="{{ user.icon_url }}">  <!-- 追加 -->
  <a href="{% url 'logout' %}">ログアウト</a>
{% else %}
  <a href="{% url 'social:begin' 'line' %}">LINEでログインする</a>
{% endif %}