diff --git a/dev/auth_style_design/migrations/0002_default_allauth.py b/dev/auth_style_design/migrations/0002_default_allauth.py new file mode 100644 index 0000000..491d5d6 --- /dev/null +++ b/dev/auth_style_design/migrations/0002_default_allauth.py @@ -0,0 +1,44 @@ +from django.db import migrations +from django.db.backends.base.schema import BaseDatabaseSchemaEditor +from django.db.migrations.state import StateApps + + +def create_default_allauth(apps: StateApps, schema_editor: BaseDatabaseSchemaEditor): + User = apps.get_model('auth', 'User') # noqa: N806 + EmailAddress = apps.get_model('account', 'EmailAddress') # noqa: N806 + + user = User.objects.create_user( + username='user@auth-style-design.test', + email='user@auth-style-design.test', + password='password', + first_name='John', + last_name='Doe', + ) + EmailAddress.objects.create( + user=user, + email=user.email, + verified=True, + primary=True, + ) + + +def delete_default_allauth(apps: StateApps, schema_editor: BaseDatabaseSchemaEditor): + User = apps.get_model('auth', 'User') # noqa: N806 + + User.objects.filter(email='user@auth-style-design.test').delete() + # EmailAddress will cascade delete + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth_style_design', '0001_default_site'), + # This is the final auth app migration + ('auth', '0012_alter_user_first_name_max_length'), + # This is the final account (Allauth) app migration + ('account', '0002_email_max_length'), + ] + + operations = [ + migrations.RunPython(create_default_allauth, delete_default_allauth), + ] diff --git a/dev/auth_style_design/migrations/0003_default_oauth_application.py b/dev/auth_style_design/migrations/0003_default_oauth_application.py new file mode 100644 index 0000000..4742973 --- /dev/null +++ b/dev/auth_style_design/migrations/0003_default_oauth_application.py @@ -0,0 +1,39 @@ +from django.db import migrations +from django.db.backends.base.schema import BaseDatabaseSchemaEditor +from django.db.migrations.state import StateApps + + +def create_default_oauth_application(apps: StateApps, schema_editor: BaseDatabaseSchemaEditor): + User = apps.get_model('auth', 'User') # noqa: N806 + Application = apps.get_model('oauth2_provider', 'Application') # noqa: N806 + + user = User.objects.get(email='user@auth-style-design.test') + Application.objects.create( + user=user, + redirect_uris='http://127.0.0.1:8000/ urn:ietf:wg:oauth:2.0:oob', + client_type='public', # Application.CLIENT_PUBLIC + authorization_grant_type='authorization-code', # Application.GRANT_AUTHORIZATION_CODE + client_secret='', + name='auth-style-design', + ) + + +def delete_default_oauth_application(apps: StateApps, schema_editor: BaseDatabaseSchemaEditor): + Application = apps.get_model('oauth2_provider', 'Application') # noqa: N806 + + Application.objects.filter(name='auth-style-design').delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth_style_design', '0002_default_allauth'), + # This is the final auth app migration + ('auth', '0012_alter_user_first_name_max_length'), + # This is the final oauth2_provider app migration + ('oauth2_provider', '0005_auto_20211222_2352'), + ] + + operations = [ + migrations.RunPython(create_default_oauth_application, delete_default_oauth_application), + ] diff --git a/dev/auth_style_design/templates/auth_style_design/auth_template_listing.html b/dev/auth_style_design/templates/auth_style_design/auth_template_listing.html index c2b0571..1cb47c4 100644 --- a/dev/auth_style_design/templates/auth_style_design/auth_template_listing.html +++ b/dev/auth_style_design/templates/auth_style_design/auth_template_listing.html @@ -28,4 +28,3 @@

Auth templates

- diff --git a/dev/auth_style_design/views.py b/dev/auth_style_design/views.py index 8d3186f..755e814 100644 --- a/dev/auth_style_design/views.py +++ b/dev/auth_style_design/views.py @@ -1,39 +1,83 @@ +import urllib.parse + from django.shortcuts import render -from django.urls import reverse_lazy +from django.urls import reverse, reverse_lazy +from oauth2_provider.models import Application + + +def _oauth2_provider_authorize_url() -> str: + base_url = reverse('oauth2_provider:authorize') + qs = urllib.parse.urlencode( + { + 'client_id': Application.objects.get(name='auth-style-design').client_id, + 'response_type': 'code', + # No redirect_uri required, it will use the value from the Application + # Other arguments are optional to render a GET + } + ) + return f'{base_url}?{qs}' + + +def _oauth2_provider_authorize_oob_url() -> str: + base_url = reverse('oauth2_provider:authorize') + qs = urllib.parse.urlencode( + { + 'client_id': Application.objects.get(name='auth-style-design').client_id, + 'response_type': 'code', + 'redirect_uri': 'urn:ietf:wg:oauth:2.0:oob', + # Other arguments are optional to render a GET + } + ) + return f'{base_url}?{qs}' -reverse_lazy('') template_files = { - 'account_inactive.html': reverse_lazy('account_inactive'), - 'base.html': None, - 'email.html': reverse_lazy('account_email'), + # Allauth + 'account/account_inactive.html': reverse_lazy('account_inactive'), + 'account/base.html': None, + 'account/email.html': reverse_lazy('account_email'), # TODO: valid key - 'email_confirm.html': reverse_lazy( + 'account/email_confirm.html': reverse_lazy( 'account_confirm_email', kwargs={'key': 'secret-key'} ), # invalid? - 'login.html': reverse_lazy('account_login'), - 'logout.html': reverse_lazy('account_logout'), # broken - 'password_change.html': reverse_lazy('account_change_password'), # redirects - 'password_reset.html': reverse_lazy('account_reset_password'), - 'password_reset_done.html': reverse_lazy('account_reset_password_done'), + 'account/login.html': reverse_lazy('account_login'), + 'account/logout.html': reverse_lazy('account_logout'), # broken + 'account/password_change.html': reverse_lazy('account_change_password'), # redirects + 'account/password_reset.html': reverse_lazy('account_reset_password'), + 'account/password_reset_done.html': reverse_lazy('account_reset_password_done'), # TODO: valid key - 'password_reset_from_key.html': reverse_lazy( + 'account/password_reset_from_key.html': reverse_lazy( 'account_reset_password_from_key', kwargs={'uidb36': '0', 'key': 'secret-key'} ), # invalid? - 'password_reset_from_key_done.html': reverse_lazy('account_reset_password_from_key_done'), + 'account/password_reset_from_key_done.html': reverse_lazy( + 'account_reset_password_from_key_done' + ), # 'account_set_password' redirects to 'account_change_password' if the user has a password # TODO: this doesn't render the form on the page - 'password_set.html': reverse_lazy( + 'account/password_set.html': reverse_lazy( 'auth-template-file', kwargs={'template_file': 'password_set.html'} ), - 'signup.html': reverse_lazy('account_signup'), - 'signup_closed.html': reverse_lazy( + 'account/signup.html': reverse_lazy('account_signup'), + 'account/signup_closed.html': reverse_lazy( 'auth-template-file', kwargs={'template_file': 'signup_closed.html'} ), - 'verification_sent.html': reverse_lazy('account_email_verification_sent'), - 'verified_email_required.html': reverse_lazy( + 'account/verification_sent.html': reverse_lazy('account_email_verification_sent'), + 'account/verified_email_required.html': reverse_lazy( 'auth-template-file', kwargs={'template_file': 'verified_email_required.html'} ), + # OAuth Toolkit + # https://django-oauth-toolkit.readthedocs.io/en/latest/templates.html + 'oauth2_provider/base.html': None, + 'oauth2_provider/authorize.html': _oauth2_provider_authorize_url, + 'oauth2_provider/authorize.html (invalid)': reverse_lazy('oauth2_provider:authorize'), + 'oauth2_provider/authorized-oob.html': _oauth2_provider_authorize_oob_url, + # TODO: create a token for these to use + # 'oauth2_provider/authorized-tokens.html': reverse_lazy( + # 'oauth2_provider:authorized-token-list' + # ), + # 'oauth2_provider/authorized-token-delete.html': reverse_lazy( + # 'oauth2_provider:authorized-token-delete', kwargs={'pk': 0} + # ), } diff --git a/dev/settings.py b/dev/settings.py index 516be46..2cf3eab 100644 --- a/dev/settings.py +++ b/dev/settings.py @@ -32,7 +32,7 @@ class MinimalDevelopmentBaseConfiguration( INTERNAL_IPS = _AlwaysContains() if _is_docker() else ['127.0.0.1'] OAUTH2_PROVIDER = { - 'PKCE_REQUIRED': True, + 'PKCE_REQUIRED': False, 'ALLOWED_REDIRECT_URI_SCHEMES': ['http', 'https'], 'REQUEST_APPROVAL_PROMPT': 'force', }