diff --git a/src/newsreader/accounts/models.py b/src/newsreader/accounts/models.py
index a54c375..baf0b39 100644
--- a/src/newsreader/accounts/models.py
+++ b/src/newsreader/accounts/models.py
@@ -84,3 +84,7 @@ class User(AbstractUser):
def delete(self, *args, **kwargs):
self.task.delete()
return super().delete(*args, **kwargs)
+
+ @property
+ def has_twitter_auth(self):
+ return self.twitter_oauth_token and self.twitter_oauth_token_secret
diff --git a/src/newsreader/accounts/templates/accounts/views/integrations.html b/src/newsreader/accounts/templates/accounts/views/integrations.html
index c5ebea8..8bb0b2e 100644
--- a/src/newsreader/accounts/templates/accounts/views/integrations.html
+++ b/src/newsreader/accounts/templates/accounts/views/integrations.html
@@ -55,7 +55,7 @@
{% endif %}
{% if twitter_revoke_url %}
-
{% else %}
diff --git a/src/newsreader/accounts/urls.py b/src/newsreader/accounts/urls.py
index f94f5ec..9f0f2e3 100644
--- a/src/newsreader/accounts/urls.py
+++ b/src/newsreader/accounts/urls.py
@@ -21,6 +21,7 @@ from newsreader.accounts.views import (
RegistrationView,
SettingsView,
TwitterAuthRedirectView,
+ TwitterRevokeRedirectView,
TwitterTemplateView,
)
@@ -96,6 +97,11 @@ urlpatterns = [
login_required(TwitterTemplateView.as_view()),
name="twitter-template",
),
+ path(
+ "settings/integrations/twitter/revoke/",
+ login_required(TwitterRevokeRedirectView.as_view()),
+ name="twitter-revoke",
+ ),
path(
"settings/integrations",
login_required(IntegrationsView.as_view()),
diff --git a/src/newsreader/accounts/views/__init__.py b/src/newsreader/accounts/views/__init__.py
index 7c0b946..81dd1fc 100644
--- a/src/newsreader/accounts/views/__init__.py
+++ b/src/newsreader/accounts/views/__init__.py
@@ -5,6 +5,7 @@ from newsreader.accounts.views.integrations import (
RedditTemplateView,
RedditTokenRedirectView,
TwitterAuthRedirectView,
+ TwitterRevokeRedirectView,
TwitterTemplateView,
)
from newsreader.accounts.views.password import (
diff --git a/src/newsreader/accounts/views/integrations.py b/src/newsreader/accounts/views/integrations.py
index a8ec089..9a7c0be 100644
--- a/src/newsreader/accounts/views/integrations.py
+++ b/src/newsreader/accounts/views/integrations.py
@@ -23,6 +23,7 @@ from newsreader.news.collection.twitter import (
TWITTER_ACCESS_TOKEN_URL,
TWITTER_AUTH_URL,
TWITTER_REQUEST_TOKEN_URL,
+ TWITTER_REVOKE_URL,
)
from newsreader.news.collection.utils import post
@@ -69,11 +70,9 @@ class IntegrationsView(TemplateView):
def get_twitter_context(self, **kwargs):
twitter_revoke_url = None
- user = self.request.user
- # TODO add revoke url
- if user.twitter_oauth_token and user.twitter_oauth_token_secret:
- twitter_revoke_url = "https://foo/bar"
+ if self.request.user.has_twitter_auth:
+ twitter_revoke_url = reverse_lazy("accounts:twitter-revoke")
return {
"twitter_request_url": reverse_lazy("accounts:twitter-request"),
@@ -176,6 +175,39 @@ class RedditRevokeRedirectView(RedirectView):
return response
+# TODO write tests
+class TwitterRevokeRedirectView(RedirectView):
+ url = reverse_lazy("accounts:integrations")
+
+ def get(self, request, *args, **kwargs):
+ if not request.user.has_twitter_auth:
+ messages.error(request, _("No twitter credentials found"))
+ return super().get(request, *args, **kwargs)
+
+ oauth = OAuth(
+ settings.TWITTER_CONSUMER_ID,
+ client_secret=settings.TWITTER_CONSUMER_SECRET,
+ resource_owner_key=request.user.twitter_oauth_token,
+ resource_owner_secret=request.user.twitter_oauth_token_secret,
+ )
+
+ try:
+ post(TWITTER_REVOKE_URL, auth=oauth)
+ except StreamException:
+ logger.exception("Failed revoking Twitter account")
+
+ messages.error(request, _("Unable revoke Twitter account"))
+ return super().get(request, *args, **kwargs)
+
+ request.user.twitter_oauth_token = None
+ request.user.twitter_oauth_token_secret = None
+ request.user.save()
+
+ messages.success(request, _("Twitter account revoked"))
+ return super().get(request, *args, **kwargs)
+
+
+# TODO write tests
class TwitterAuthRedirectView(RedirectView):
url = reverse_lazy("accounts:integrations")
@@ -210,6 +242,7 @@ class TwitterAuthRedirectView(RedirectView):
# TODO remove cached tokens
+# TODO write tests
class TwitterTemplateView(TemplateView):
template_name = "accounts/views/twitter.html"
diff --git a/src/newsreader/news/collection/twitter.py b/src/newsreader/news/collection/twitter.py
index 7f075d7..bfd4cbf 100644
--- a/src/newsreader/news/collection/twitter.py
+++ b/src/newsreader/news/collection/twitter.py
@@ -34,6 +34,7 @@ TWITTER_API_URL = "https://api.twitter.com/1.1"
TWITTER_REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token"
TWITTER_AUTH_URL = "https://api.twitter.com/oauth/authorize"
TWITTER_ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token"
+TWITTER_REVOKE_URL = f"{TWITTER_API_URL}/oauth/invalidate_token"
class TwitterBuilder(PostBuilder):