Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RT-147 allauth #185

Merged
merged 31 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6d9acd8
Respect "use_celery" setting when setting in structlog and sentry
kkowalski-reef May 13, 2024
fe56952
Allauth integration option
kkowalski-reef May 19, 2024
ae5aa8e
Merge branch 'refs/heads/master' into RT-147_allauth
kkowalski-reef May 29, 2024
2b3826b
Try fix whitespace
kkowalski-reef May 29, 2024
9dc79bb
Try fix whitespace again
kkowalski-reef Jun 1, 2024
0c264f2
Readme for allauth
kkowalski-reef Jun 1, 2024
ac59fdd
Readme for allauth
kkowalski-reef Jun 1, 2024
37bb85a
PR cleanup
kkowalski-reef Jun 10, 2024
f5a76a9
Remove openid connect provider enabled by default
kkowalski-reef Jun 10, 2024
28a1c08
(WIP) Common auth providers
kkowalski-reef Jun 10, 2024
2b157f2
Fix missing endif; attempt whitespace fix
kkowalski-reef Jun 29, 2024
25ef0c3
Improve whitespace in .env
kkowalski-reef Jun 29, 2024
0246edb
Use twitter_oauth2 instead of twitter provider for twitter auth
kkowalski-reef Jun 29, 2024
4813476
Use emails as usernames
kkowalski-reef Jun 29, 2024
8899215
Remove django auth URLs when allauth is enabled
kkowalski-reef Jun 30, 2024
8dd2316
Redirect to / after login
kkowalski-reef Jun 30, 2024
ef905c1
Merge branch 'refs/heads/master' into RT-147_allauth
kkowalski-reef Jun 30, 2024
249ea47
Attempt fix whitespace lint
kkowalski-reef Jun 30, 2024
ec16cd2
Change default login URL when allauth is enabled
kkowalski-reef Jun 30, 2024
18f5df2
Change default login URL when allauth is enabled
kkowalski-reef Jun 30, 2024
e8313d2
Change default login URL when allauth is enabled
kkowalski-reef Jun 30, 2024
81e6d7e
Fix lint again
kkowalski-reef Jun 30, 2024
3380ca1
Updated generic openid_connect base config
kkowalski-reef Jun 30, 2024
eb6e5e9
Updated readme
kkowalski-reef Jun 30, 2024
32f3c19
Add option to trust emails from SSO providers
kkowalski-reef Jun 30, 2024
d82c5fe
Whitespace
kkowalski-reef Jun 30, 2024
761a760
Use a comma-separated list of allauth providers in cookiecutter
kkowalski-reef Jul 2, 2024
41cc968
if/else for allauth/django auth routes
kkowalski-reef Jul 2, 2024
b23bd21
Wrap external generic auth setup in details block
kkowalski-reef Jul 2, 2024
dcdafe7
Fixed bad template comparisons
kkowalski-reef Jul 2, 2024
2d51300
Merge branch 'refs/heads/master' into RT-147_allauth
kkowalski-reef Jul 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@
"use_flower": "n",
"use_fingerprinting": "n",
"use_channels": "y",
"use_allauth": "n",
"allauth_trust_external_emails": "y",
"use_allauth_apple": "n",
"use_allauth_atlassian": "n",
"use_allauth_discord": "n",
"use_allauth_facebook": "n",
"use_allauth_github": "n",
"use_allauth_gitlab": "n",
"use_allauth_google": "n",
"use_allauth_microsoft": "n",
"use_allauth_openid_connect": "n",
"use_allauth_twitter": "n",
kkowalski-reef marked this conversation as resolved.
Show resolved Hide resolved
"sentry_dsn": "",
"csp_enabled": "n",
"csp_report_only": "y",
Expand Down
39 changes: 39 additions & 0 deletions {{cookiecutter.repostory_name}}/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,45 @@ If one wants to deploy other branch, force may be used to push desired branch to
```sh
git push --force production local-branch-to-deploy:master
```
{% if cookiecutter.use_allauth == 'y' %}
# External auth (OAuth, OpenID connect etc.)
kkowalski-reef marked this conversation as resolved.
Show resolved Hide resolved
To configure an external authentication mechanism, usually you must acquire a "client ID" and a "client secret".
This usually requires registering your application on the provider's website. Look at allauth's documentation for the
specific provider to see how to do that:

[https://docs.allauth.org/en/latest/socialaccount/providers/index.html](https://docs.allauth.org/en/latest/socialaccount/providers/index.html)

After acquiring the id and secret, simply fill in the env vars for the provider.
{% if cookiecutter.allauth_trust_external_emails == "y" %}

> ⚠️ Caution: the SSO provider is trusted to have verified the ownership of user's email address.
> This will allow a user to log in to any account that matches the email address returned by the
> SSO provider, whether the account is connected with the provider or not.

{% endif %}
{% if cookiecutter.use_allauth_openid_connect == "y" %}
## Setting up a generic OpenID Connect service
If an SSO provider supports the OIDC protocol, it can be set up as a generic OIDC provider here:

1. Come up with a new `provider_id`
- it's just an arbitrary alphanumerical string to identify the provider in the app
- it must be unique in the scope of the app
- it should not collide with the name of an installed provider type - so don't use `gitlab`, `google` or similar
- something like `rt_keycloak` would be OK
2. Register the app with the provider to acquire a `client_id`, a `secret` and the URL for the openid config (e.g. https://gitlab.com/.well-known/openid-configuration)
- When asked for callback / redirect url, use `https://{domain}/accounts/oidc/{provider_id}/login/callback/`
- For development, usually http://127.0.0.1:8000 can be used as the base URL here
3. Fill in the `OPENID_CONNECT_*` env vars
- `OPENID_CONNECT_NICE_NAME` is just a human-readable name, it will be later shown on login form (Log in with {name}...)
- the `OPENID_CONNECT_SERVER_URL` value is just the URL **before** the .well-known part, so for https://gitlab.com/.well-known/openid-configuration this is just https://gitlab.com

{% endif %}
## Allauth users in django
1. Allauth does not disable django's authentication. It lives next to it as an alternative. You can still access django admin login.
2. Allauth "social users" are just an extension to regular django users. When someone logs in via allauth, a django user model will also be created for them.
3. A "profile" page is available at `/accounts/`

{% endif %}
{% if cookiecutter.monitoring == 'y' %}
# Monitoring

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

{% if cookiecutter.use_celery == "y" -%}
# from celery.schedules import crontab
{% endif %}
{%- if cookiecutter.use_allauth == "y" -%}
from django.urls import reverse_lazy
{% endif -%}
import structlog

Expand Down Expand Up @@ -59,6 +62,13 @@ def wrapped(*args, **kwargs):

ALLOWED_HOSTS = ["*"]

AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
{%- if cookiecutter.use_allauth == "y" %}
"allauth.account.auth_backends.AuthenticationBackend",
{%- endif %}
]

INSTALLED_APPS = [
{%- if cookiecutter.use_channels == "y" %}
"daphne",
Expand Down Expand Up @@ -87,9 +97,44 @@ def wrapped(*args, **kwargs):
"django_probes",
"django_structlog",
"constance",
{% if cookiecutter.use_fingerprinting == "y" -%}
{%- if cookiecutter.use_fingerprinting == "y" %}
"fingerprint",
{% endif -%}
{%- endif %}
{%- if cookiecutter.use_allauth == "y" %}
"allauth",
"allauth.account",
"allauth.socialaccount",
{%- if cookiecutter.use_allauth_apple == "y" %}
"allauth.socialaccount.providers.apple",
{%- endif %}
{%- if cookiecutter.use_allauth_discord == "y" %}
"allauth.socialaccount.providers.discord",
{%- endif %}
{%- if cookiecutter.use_allauth_facebook == "y" %}
"allauth.socialaccount.providers.facebook",
{%- endif %}
{%- if cookiecutter.use_allauth_github == "y" %}
"allauth.socialaccount.providers.github",
{%- endif %}
{%- if cookiecutter.use_allauth_gitlab == "y" %}
"allauth.socialaccount.providers.gitlab",
{%- endif %}
{%- if cookiecutter.use_allauth_google == "y" %}
"allauth.socialaccount.providers.google",
{%- endif %}
{%- if cookiecutter.use_allauth_microsoft == "y" %}
"allauth.socialaccount.providers.microsoft",
{%- endif %}
{%- if cookiecutter.use_allauth_openid_connect == "y" %}
"allauth.socialaccount.providers.openid_connect",
kkowalski-reef marked this conversation as resolved.
Show resolved Hide resolved
{%- endif %}
{%- if cookiecutter.use_allauth_twitter == "y" %}
"allauth.socialaccount.providers.twitter_oauth2",
{%- endif %}
{%- if cookiecutter.use_allauth_atlassian == "y" %}
"allauth.socialaccount.providers.atlassian",
{%- endif %}
{%- endif %}
"{{cookiecutter.django_project_name}}.{{cookiecutter.django_default_app_name}}",
]

Expand Down Expand Up @@ -131,6 +176,9 @@ def wrapped(*args, **kwargs):
"django_prometheus.middleware.PrometheusAfterMiddleware",
{%- endif %}
"django_structlog.middlewares.RequestMiddleware",
{%- if cookiecutter.use_allauth == "y" -%}
"allauth.account.middleware.AccountMiddleware",
{%- endif %}
]


Expand Down Expand Up @@ -297,6 +345,8 @@ def wrapped(*args, **kwargs):
CELERY_RESULT_SERIALIZER = "json"
CELERY_WORKER_PREFETCH_MULTIPLIER = env.int("CELERY_WORKER_PREFETCH_MULTIPLIER", default=10)
CELERY_BROKER_POOL_LIMIT = env.int("CELERY_BROKER_POOL_LIMIT", default=50)

DJANGO_STRUCTLOG_CELERY_ENABLED = True
agoncharov-reef marked this conversation as resolved.
Show resolved Hide resolved
{%- endif %}

EMAIL_BACKEND = env("EMAIL_BACKEND")
Expand Down Expand Up @@ -351,7 +401,6 @@ def wrapped(*args, **kwargs):
},
},
}
DJANGO_STRUCTLOG_CELERY_ENABLED = True


def configure_structlog():
Expand All @@ -378,7 +427,9 @@ def configure_structlog():
# Sentry
if SENTRY_DSN := env("SENTRY_DSN", default=""):
import sentry_sdk
{% if cookiecutter.use_celery == "y" -%}
from sentry_sdk.integrations.celery import CeleryIntegration
{% endif -%}
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import LoggingIntegration, ignore_logger
from sentry_sdk.integrations.redis import RedisIntegration
Expand All @@ -388,7 +439,9 @@ def configure_structlog():
environment=ENV,
integrations=[
DjangoIntegration(),
{% if cookiecutter.use_celery == "y" -%}
CeleryIntegration(),
{% endif -%}
RedisIntegration(),
LoggingIntegration(
level=logging.INFO, # Capture info and above as breadcrumbs
Expand All @@ -397,3 +450,112 @@ def configure_structlog():
],
)
ignore_logger("django.security.DisallowedHost")
{% if cookiecutter.use_allauth == "y" -%}
LOGIN_URL = reverse_lazy("account_login")
LOGIN_REDIRECT_URL = "/"
ACCOUNT_AUTHENTICATION_METHOD = "email"
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_CHANGE_EMAIL = False
ACCOUNT_MAX_EMAIL_ADDRESSES = 1
{%- if cookiecutter.allauth_trust_external_emails == "y" %}
# Trust, that the configured SSO providers verify that the users own the addresses that we get from the SSO flow.
# This allows users to log in to any existing account with any configured provider if the email addresses match.
SOCIALACCOUNT_EMAIL_AUTHENTICATION: True
{%- endif %}
SOCIALACCOUNT_PROVIDERS = {
{%- if cookiecutter.use_allauth_apple == "y" %}
"apple": {
"APP": {
"client_id": env("APPLE_LOGIN_CLIENT_ID"),
"secret": env("APPLE_LOGIN_SECRET"),
"key": env("APPLE_LOGIN_KEY"),
"settings": {
"certificate_key": env("APPLE_LOGIN_CERTIFICATE_PRIVATE_KEY"),
},
},
},
{%- endif %}
{%- if cookiecutter.use_allauth_discord == "y" %}
"discord": {
"APP": {
"client_id": env("DISCORD_LOGIN_CLIENT_ID"),
"secret": env("DISCORD_LOGIN_SECRET"),
},
},
{%- endif %}
{%- if cookiecutter.use_allauth_facebook == "y" %}
"facebook": {
"APP": {
"client_id": env("FACEBOOK_LOGIN_CLIENT_ID"),
"secret": env("FACEBOOK_LOGIN_SECRET"),
},
},
{%- endif %}
{%- if cookiecutter.use_allauth_github == "y" %}
"github": {
"APP": {
"client_id": env("GITHUB_LOGIN_CLIENT_ID"),
"secret": env("GITHUB_LOGIN_SECRET"),
},
},
{%- endif %}
{%- if cookiecutter.use_allauth_gitlab == "y" %}
"gitlab": {
"APP": {
"client_id": env("GITLAB_LOGIN_CLIENT_ID"),
"secret": env("GITLAB_LOGIN_SECRET"),
},
},
{%- endif %}
{%- if cookiecutter.use_allauth_google == "y" %}
"google": {
"APP": {
"client_id": env("GOOGLE_LOGIN_CLIENT_ID"),
"secret": env("GOOGLE_LOGIN_SECRET"),
},
},
{%- endif %}
{%- if cookiecutter.use_allauth_microsoft == "y" %}
"microsoft": {
"APP": {
"client_id": env("MICROSOFT_LOGIN_CLIENT_ID"),
"secret": env("MICROSOFT_LOGIN_SECRET"),
"settings": {
"tenant": "organizations",
},
},
},
{%- endif %}
{%- if cookiecutter.use_allauth_twitter == "y" %}
"twitter_oauth2": {
"APP": {
"client_id": env("TWITTER_LOGIN_CLIENT_ID"),
"secret": env("TWITTER_LOGIN_SECRET"),
},
},
{%- endif %}
{%- if cookiecutter.use_allauth_atlassian == "y" %}
"atlassian": {
"APP": {
"client_id": env("ATLASSIAN_LOGIN_CLIENT_ID"),
"secret": env("ATLASSIAN_LOGIN_SECRET"),
},
},
{%- endif %}
{%- if cookiecutter.use_allauth_openid_connect == "y" %}
"openid_connect": {
"APP": {
"client_id": "oidc",
"name": env("OPENID_CONNECT_NICE_NAME"),
"secret": env("OPENID_CONNECT_LOGIN_SECRET"),
"settings": {
"server_url": env("OPENID_CONNECT_SERVER_URL")
}
},
},
{%- endif %}
}
{%- endif %}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

urlpatterns = [
path("admin/", site.urls),
{%- if cookiecutter.use_allauth != "y" %}
kkowalski-reef marked this conversation as resolved.
Show resolved Hide resolved
path("", include("django.contrib.auth.urls")),
{%- endif %}
{%- if cookiecutter.use_fingerprinting == "y" %}
path("redirect/", FingerprintView.as_view(), name="fingerprint"),
{%- endif %}
Expand All @@ -25,6 +27,9 @@
path("business-metrics", metrics_manager.view, name="prometheus-business-metrics"),
path("healthcheck/", include("health_check.urls")),
{%- endif %}
{%- if cookiecutter.use_allauth == "y" %}
path('accounts/', include('allauth.urls')),
{%- endif %}
]

{%- if cookiecutter.use_channels == "y" %}
Expand Down
47 changes: 47 additions & 0 deletions {{cookiecutter.repostory_name}}/envs/dev/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,50 @@ BACKUP_B2_BUCKET=
BACKUP_B2_KEY_ID=
BACKUP_B2_KEY_SECRET=
BACKUP_LOCAL_ROTATE_KEEP_LAST=

{% if cookiecutter.use_allauth == "y" -%}
{%- if cookiecutter.use_allauth_apple == "y" -%}
APPLE_LOGIN_CLIENT_ID=
APPLE_LOGIN_SECRET=
APPLE_LOGIN_KEY=
APPLE_LOGIN_CERTIFICATE_PRIVATE_KEY=
{% endif %}
kkowalski-reef marked this conversation as resolved.
Show resolved Hide resolved
{%- if cookiecutter.use_allauth_discord == "y" -%}
DISCORD_LOGIN_CLIENT_ID=
DISCORD_LOGIN_SECRET=
{% endif %}
{%- if cookiecutter.use_allauth_facebook == "y" -%}
FACEBOOK_LOGIN_CLIENT_ID=
FACEBOOK_LOGIN_SECRET=
{% endif %}
{%- if cookiecutter.use_allauth_github == "y" -%}
GITHUB_LOGIN_CLIENT_ID=
GITHUB_LOGIN_SECRET=
{% endif %}
{%- if cookiecutter.use_allauth_gitlab == "y" -%}
GITLAB_LOGIN_CLIENT_ID=
GITLAB_LOGIN_SECRET=
{% endif %}
{%- if cookiecutter.use_allauth_google == "y" -%}
GOOGLE_LOGIN_CLIENT_ID=
GOOGLE_LOGIN_SECRET=
{% endif %}
{%- if cookiecutter.use_allauth_microsoft == "y" -%}
MICROSOFT_LOGIN_CLIENT_ID=
MICROSOFT_LOGIN_SECRET=
{% endif %}
{%- if cookiecutter.use_allauth_openid_connect == "y" -%}
OPENID_CONNECT_NICE_NAME=
OPENID_CONNECT_LOGIN_CLIENT_ID=
OPENID_CONNECT_LOGIN_SECRET=
OPENID_CONNECT_SERVER_URL=
{% endif %}
{%- if cookiecutter.use_allauth_twitter == "y" -%}
TWITTER_LOGIN_CLIENT_ID=
TWITTER_LOGIN_SECRET=
{% endif %}
{%- if cookiecutter.use_allauth_atlassian == "y" -%}
ATLASSIAN_LOGIN_CLIENT_ID=
ATLASSIAN_LOGIN_SECRET=
{% endif %}
{% endif %}
Loading
Loading