本記事のゴール
- OAuthの認可サーバーに対してリクエストをしてアクセストークンが返ってくること
- OAuthのリクエスト&レスポンスをDjango Rest Swaggerのdocsに表示させること
環境
python==3.7.2
Django==2.1.7
django-rest-framework==3.9.3
django-rest-swagger==2.2.0
django-oauth-toolkit==1.2.0
OAuth認証には
django-oauth-toolkitを使用します
パッケージによるDRF認証
解説
基本的な設定
基本的には公式に習ってます
細かいところは独自に実装したので、その辺も踏まえて解説します
公式の1分でプロバイダを作るという項目があるように、このページを見れば簡単に実装できます
上記の項目を見れば誰でもできるので、この辺りは駆け足で解説します
INSTALLED_APPS = [
'django.contrib.admin',
'oauth2_provider',
'corsheaders',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
)
}
OAUTH2_PROVIDER = {
# 発行するアクセストークンにアクセス制御をつけるオプション
'SCOPES': {
'read': 'Read scope',
'write': 'Write scope',
'groups': 'Access to your groups'
},
# アクセストークンの有効期限秒数 2592000 -> 1ヶ月
'ACCESS_TOKEN_EXPIRE_SECONDS': 2592000
プロジェクトルートのurls.pyを以下のように設定します
urlpatterns = [
path('oauth/', include('oauth2_provider.urls', namespace='oauth2_provider')),
]
クライアントID・クライアントシークレットの生成
完了後にmigrateするとテーブルが4つ追加されます
これらはクライアントID・アクセストーンなどの情報を保存するためのテーブルです。
その後、スーパーユーザーで以下のURLへアクセスしてください
http://127.0.0.1:8000/oauth/applications/
こんな画面が出ると思います
Click here
を押してアプリケーション登録をしましょう。
name
-> 何でもいいです
Client id
-> 自動で生成されます
Client secret
-> 自動で生成されます
Client type
-> confidential
Authorization grant type
-> Recource ~~ passwordを選択
Redirect urls
-> 空欄で大丈夫です
Save
を押すと情報がoauth_provider.applicationテーブルに保存されます
これでアプリケーション登録は完了です。
アクセストークン発行
次は生成したクライアント情報を使用してアクセストークンを発行しましょう。
http通信ができれば何でもいいのでコマンドでもいいのですが、個人的にはPostmanがオススメです
レスポンスにアクセストークンが返ってきています
有効期限(expires_in)もsettings.py
で指定した値となっていますね
この時のレスポンス情報はoauth_prodider.accesstokenテーブルに保存されますので、ご確認ください
以上で基本的な説明は終わりですが、ここからはこのリクエスト&レスポンスをDjango Rest Swaggerに表示させるところまでやります
そこも知りたい!という方はもう少しお付き合いください。
Oauth認証のリクエスト&レスポンスをdocsへ表示
dJango-oauth-toolkitのパッケージにはDRF用に作成されたシリアライザーなどがないので、独自に実装する必要があります
djangorest-jwtは事前に用意されているので、docsに表示させるのも簡単にできるのですが・・・。
多少強引な実装となっておりますが、それでも良いって方はぜひ。
まずはシリアライザーがないとどうしようもないので、作りましょう
class OAuthPostTokenSerializer(serializers.Serializer):
grant_type = serializers.CharField(required=True)
username = serializers.CharField(required=True)
password = serializers.CharField(required=True)
client_id = serializers.CharField(required=True)
client_secret = serializers.CharField(required=True)
次にシリアライザーを使ってviewを作ります
from rest_framework.generics import CreateAPIView
from .serializers import OAuthPostTokenSerializer
class OAuthPostToken(CreateAPIView):
"""ユーザー認証をしてアクセストークンを返す"""
serializer_class = OAuthPostTokenSerializer
def post(self, request, *args, **kwargs):
protocol = self.request.scheme
host = request.get_host()
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
r = requests.post(protocol + '://' + host + '/oauth/token/',
data={
'grant_type': 'password',
'username': OAUTH_USERNAME,
'password': OAUTH_PASSWORD,
'client_id': OAUTH_CLIENT_ID,
'client_secret': OAUTH_CLIENT_SECRET,
},
)
return Response(r.json(), status=status.HTTP_200_OK)
else:
return Response(serializer.errors)
一旦docsに持ってきてOauth認可サーバーに渡すという男らしい実装です笑
grant_type
, client_id
, client_secret
は固定なので、環境変数として始めから値を持ってしまっても良いでしょう
最後にurls.pyに実装したviewを追加しましょう
API_ROOT = 'api/test/'
urlpatterns = [
path(API_ROOT + 'oauth/token/', OAuthPostToken.as_view()), # <- 追加
path('oauth/', include('oauth2_provider.urls', namespace='oauth2_provider')),
]
するとdocsにOauthが追加されています
tips1 レスポンスににユーザー情報も一緒に返す
レスポンスとしてアクセストークンが返ってきますが、そこにユーザー情報も付与したいという方向け
アクセストークンレスポンスはTokenView
というviewを使用しているのでそれを継承して自分でカスタマイズしましょう
以下は例です
class OAuthResponseTokenView(TokenView):
"""OAuthのトークンレスポンスにユーザー情報を追加して返す処理"""
@method_decorator(sensitive_post_parameters("password"))
def post(self, request, *args, **kwargs):
url, headers, body, status = self.create_token_response(request)
if status == 200:
body = json.loads(body)
access_token = body.get("access_token")
if access_token is not None:
token = get_access_token_model().objects.get(
token=access_token)
app_authorized.send(
sender=self, request=request,
token=token)
body['user_info'] = {
'id': token.user.id,
'username': token.user.username,
'email': token.user.email
}
body = json.dumps(body)
response = HttpResponse(content=body, status=status)
for k, v in headers.items():
response[k] = v
return response
path(API_ROOT + 'oauth/token/', users_views.OAuthPostToken.as_view()),
path('oauth/token/', users_views.OAuthResponseTokenView.as_view()), # <-追加
path('oauth/', include('oauth2_provider.urls', namespace='oauth2_provider')),
終わりに
多少荒い説明となってしまいましたが、いかがでしょうか
上記の方法でリフレッシュトークンによるアクセストークン発行も可能です
リソースサーバーに対してアクセストークンを使用した認証方法は時間があったら記事にしようと思います