Last active
December 10, 2018 12:49
-
-
Save ryu22e/f5be471c8bb36192c29efdb3688a9ec5 to your computer and use it in GitHub Desktop.
WebAuthnサインイン(サーバーサイド)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""users/backends.py""" | |
from django.conf import settings | |
from fido2.server import Fido2Server, RelyingParty | |
from .models import User, WebAuthnPublicKey | |
rp = RelyingParty(settings.RELYING_PARTY_DOMAIN, settings.RELYING_PARTY_NAME) | |
server = Fido2Server(rp) | |
class WebAuthnBackend: | |
def authenticate(self, request, username, credential_id, challenge, client_data, auth_data, signature): | |
try: | |
user = User.objects.get(username=username) | |
except User.DoesNotExist: | |
return None | |
credentials = ( | |
WebAuthnPublicKey. | |
objects. | |
credentials(username) | |
) | |
try: | |
server.authenticate_complete( | |
credentials, | |
credential_id, | |
challenge, | |
client_data, | |
auth_data, | |
signature | |
) | |
except ValueError: | |
return None | |
return user | |
def get_user(self, user_id): | |
try: | |
return User.objects.get(pk=user_id) | |
except User.DoesNotExist: | |
return None |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""users/serializers.py""" | |
from rest_framework import serializers | |
class AuthenticateBeginSerializer(serializers.Serializer): | |
username = serializers.CharField(max_length=150) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""users/urls.py""" | |
from django.urls import include, path | |
from . import views | |
app_name = 'users' | |
urlpatterns = [ | |
path('signin/', views.SignInView.as_view(), name='signin'), | |
path( | |
'apis/', | |
include( | |
[ | |
path('authenticate-begin/', views.AuthenticateBeginViewSet.as_view(), | |
name='authenticat_begin'), | |
path('authenticate-complete/', views.AuthenticateCompleteViewSet.as_view(), | |
name='authenticat_complete'), | |
], | |
), | |
), | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""users/views.py""" | |
from django.conf import settings | |
from django.contrib.auth import authenticate, login | |
from django.urls import reverse | |
from django.views import generic | |
from fido2.client import ClientData | |
from fido2.ctap2 import AuthenticatorData | |
from fido2.server import Fido2Server, RelyingParty | |
from rest_framework.response import Response | |
from rest_framework.views import APIView | |
from .forms import SignInForm | |
from .models import WebAuthnPublicKey | |
from .parsers import CBORParser | |
from .permissions import CSRFPermission | |
from .renderers import CBORRenderer | |
from .serializers import AuthenticateBeginSerializer | |
rp = RelyingParty(settings.RELYING_PARTY_DOMAIN, settings.RELYING_PARTY_NAME) | |
server = Fido2Server(rp) | |
class SignInView(generic.TemplateView): | |
template_name = 'users/signin.html' | |
def get_context_data(self, **kwargs): | |
context = super().get_context_data(**kwargs) | |
context['form'] = SignInForm() | |
return context | |
class AuthenticateBeginViewSet(APIView): | |
serializer_class = AuthenticateBeginSerializer | |
renderer_classes = (CBORRenderer,) | |
permission_classes = (CSRFPermission,) | |
def post(self, request, format=None): | |
serializer = self.serializer_class(data=request.data) | |
serializer.is_valid(raise_exception=True) | |
username = serializer.data['username'] | |
credentials = ( | |
WebAuthnPublicKey. | |
objects. | |
credentials(username) | |
) | |
auth_data = server.authenticate_begin(credentials) | |
request.session['challenge'] = auth_data['publicKey']['challenge'] | |
request.session['username'] = username | |
return Response(auth_data) | |
class AuthenticateCompleteViewSet(APIView): | |
parser_classes = (CBORParser,) | |
permission_classes = (CSRFPermission,) | |
def post(self, request, format=None): | |
data = request.data[0] | |
credential_id = data['credentialId'] | |
client_data = ClientData(data['clientDataJSON']) | |
auth_data = AuthenticatorData(data['authenticatorData']) | |
signature = data['signature'] | |
user = authenticate( | |
username=request.session.pop('username'), | |
credential_id=credential_id, | |
challenge=request.session.pop('challenge'), | |
client_data=client_data, | |
auth_data=auth_data, | |
signature=signature, | |
) | |
if user and user.is_active: | |
login(request, user) | |
return Response( | |
{ | |
'status': 'OK', | |
'redirect_to': reverse('accounts:profile'), # ログイン必須のページ | |
} | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment