diff --git a/.env.example b/.env.example index 4c1c2eefea..3983e23c7e 100644 --- a/.env.example +++ b/.env.example @@ -120,3 +120,11 @@ OTEL_SERVICE_NAME= # for your instance: # https://docs.djangoproject.com/en/3.2/ref/settings/#secure-proxy-ssl-header HTTP_X_FORWARDED_PROTO=false + +# Enable logging in and registration with OAuth 2 +OAUTH_ACTIVE=false +#OAUTH_NAME="OAuth Provider" # Displayed on Login Button as "Login with OAUTH_NAME" +#OAUTH_CLIENT_ID="" +#OAUTH_CLIENT_SECRET="" +#OAUTH_AUTHORIZE_URL="" # For mastodon use "https:///oauth/authorize" +#OAUTH_ACCESS_TOKEN_URL="" # For mastodon use "https:///oauth/token" diff --git a/bookwyrm/context_processors.py b/bookwyrm/context_processors.py index 0047bfce11..df0a1ce775 100644 --- a/bookwyrm/context_processors.py +++ b/bookwyrm/context_processors.py @@ -28,4 +28,6 @@ def site_settings(request): # pylint: disable=unused-argument "preview_images_enabled": settings.ENABLE_PREVIEW_IMAGES, "request_protocol": request_protocol, "js_cache": settings.JS_CACHE, + "oauth_active": settings.OAUTH_ACTIVE, + "oauth_name": settings.OAUTH_NAME, } diff --git a/bookwyrm/forms/landing.py b/bookwyrm/forms/landing.py index bd9884bc35..1b20146bfb 100644 --- a/bookwyrm/forms/landing.py +++ b/bookwyrm/forms/landing.py @@ -56,6 +56,20 @@ def clean(self): self.add_error("localname", _("User with this username already exists")) +class OAuthRegisterForm(CustomForm): + class Meta: + model = models.User + fields = ["localname", "email"] + help_texts = {f: None for f in fields} + + def clean(self): + """Check if the username is taken""" + cleaned_data = super().clean() + localname = cleaned_data.get("localname").strip() + if models.User.objects.filter(localname=localname).first(): + self.add_error("localname", _("User with this username already exists")) + + class InviteRequestForm(CustomForm): def clean(self): """make sure the email isn't in use by a registered user""" diff --git a/bookwyrm/oauth.py b/bookwyrm/oauth.py new file mode 100644 index 0000000000..45cf253fe6 --- /dev/null +++ b/bookwyrm/oauth.py @@ -0,0 +1,42 @@ +""" responds to various requests to oauth """ +from django.contrib.auth import login +from django.core.exceptions import ObjectDoesNotExist +from django.dispatch import receiver +from django.urls import reverse +from django.shortcuts import render, redirect +from django.template.response import TemplateResponse +from authlib.integrations.django_client import OAuth, OAuthError + +from bookwyrm import models +from bookwyrm.settings import DOMAIN + +oauth = OAuth() +oauth.register("oauth") +oauth = oauth.oauth + + +def auth(request): + try: + token = oauth.authorize_access_token(request) + except OAuthError: + data = {} + return TemplateResponse(request, "landing/login.html", data) + acct = oauth.get( + "https://raphus.social/api/v1/accounts/verify_credentials", token=token + ) + if acct.status_code == 200: + localname = dict(acct.json())["acct"] + username = "{}@{}".format(localname, DOMAIN) + try: + user = models.User.objects.get(username=username) + except ObjectDoesNotExist: + request.session["oauth-newuser"] = localname + request.session["oauth-newuser-pfp"] = dict(acct.json())["avatar"] + return redirect("oauth-register") + login(request, user) + return redirect("/") + + +def request_login(request): + redirect_uri = request.build_absolute_uri(reverse("oauth")) + return oauth.authorize_redirect(request, redirect_uri, force_login=True) diff --git a/bookwyrm/settings.py b/bookwyrm/settings.py index c66bd636b1..e9e06b6c4c 100644 --- a/bookwyrm/settings.py +++ b/bookwyrm/settings.py @@ -388,3 +388,14 @@ # Do not change this setting unless you already have an existing # user with the same username - in which case you should change it! INSTANCE_ACTOR_USERNAME = "bookwyrm.instance.actor" +OAUTH_ACTIVE = env("OAUTH_ACTIVE", False) +if OAUTH_ACTIVE: + OAUTH_NAME = env("OAUTH_NAME", "OAuth2") + AUTHLIB_OAUTH_CLIENTS = { + "oauth": { + "client_id": env("OAUTH_CLIENT_ID"), + "client_secret": env("OAUTH_CLIENT_SECRET"), + "authorize_url": env("OAUTH_AUTHORIZE_URL"), + "access_token_url": env("OAUTH_ACCESS_TOKEN_URL"), + } + } diff --git a/bookwyrm/templates/landing/login.html b/bookwyrm/templates/landing/login.html index 369a72bd28..ed20d06006 100644 --- a/bookwyrm/templates/landing/login.html +++ b/bookwyrm/templates/landing/login.html @@ -10,10 +10,15 @@

{% trans "Log in" %}

{% if login_form.non_field_errors %}

{{ login_form.non_field_errors }}

{% endif %} - + {% if show_confirmed_email %}

{% trans "Success! Email address confirmed." %}

{% endif %} + {% if oauth_active %} + + + + {% else %}
{% csrf_token %} {% if show_confirmed_email %}{% endif %} @@ -40,6 +45,7 @@

{% trans "Log in" %}

+ {% endif %} {% if site.allow_registration %} diff --git a/bookwyrm/templates/landing/oauth_register.html b/bookwyrm/templates/landing/oauth_register.html new file mode 100644 index 0000000000..215140baf2 --- /dev/null +++ b/bookwyrm/templates/landing/oauth_register.html @@ -0,0 +1,75 @@ +{% extends 'layout.html' %} +{% load i18n %} + +{% block title %}{% trans "Create an Account" %}{% endblock %} + +{% block content %} + +

{% trans "Create an Account" %}

+
+
+
+ {% if valid %} +
+
+{% csrf_token %} +
+ +
+ {{ username }} +
+

+ {% trans "Your username cannot be changed." %} +

+
+
+
+
+ +
+ + + {% include 'snippets/form_errors.html' with errors_list=register_form.email.errors id="desc_email_register" %} +
+
+ + + +
+
+ +
+
+
+
+ {% else %} +
+

{% trans "Permission Denied" %}

+

{% trans "Sorry!" %}

+
+ {% endif %} +
+
+
+
+ {% include 'snippets/about.html' %} +
+
+
+ +{% endblock %} diff --git a/bookwyrm/templates/layout.html b/bookwyrm/templates/layout.html index c3408f44e1..78c53b9ce7 100644 --- a/bookwyrm/templates/layout.html +++ b/bookwyrm/templates/layout.html @@ -115,6 +115,12 @@ + {% elif oauth_active %} + {% else %}