diff --git a/.env.example b/.env.example index 86961b9..ba1971d 100644 --- a/.env.example +++ b/.env.example @@ -23,10 +23,6 @@ DEFAULT_FROM_EMAIL=noreply@your-domain.com GOOGLE_OAUTH2_CLIENT_ID=your-google-client-id GOOGLE_OAUTH2_CLIENT_SECRET=your-google-client-secret -# Social Authentication - Facebook -FACEBOOK_APP_ID=your-facebook-app-id -FACEBOOK_APP_SECRET=your-facebook-app-secret - # Production SSL Settings (set to True in production) SECURE_SSL_REDIRECT=False diff --git a/apps/accounts/management/commands/setup_social_apps.py b/apps/accounts/management/commands/setup_social_apps.py new file mode 100644 index 0000000..36f6084 --- /dev/null +++ b/apps/accounts/management/commands/setup_social_apps.py @@ -0,0 +1,36 @@ +from django.core.management.base import BaseCommand +from allauth.socialaccount.models import SocialApp +from django.contrib.sites.models import Site +from decouple import config + + +class Command(BaseCommand): + help = 'Set up social authentication applications' + + def handle(self, *args, **options): + site = Site.objects.get_current() + + # Create Google OAuth2 app if it doesn't exist + google_app, created = SocialApp.objects.get_or_create( + provider='google', + name='Google', + defaults={ + 'client_id': config('GOOGLE_OAUTH2_CLIENT_ID', default='your-google-client-id'), + 'secret': config('GOOGLE_OAUTH2_CLIENT_SECRET', default='your-google-client-secret'), + } + ) + google_app.sites.add(site) + + if created: + self.stdout.write( + self.style.SUCCESS('Created Google OAuth2 application') + ) + else: + self.stdout.write( + self.style.WARNING('Google OAuth2 application already exists') + ) + + + self.stdout.write( + self.style.SUCCESS('Social applications setup completed!') + ) \ No newline at end of file diff --git a/cookies.txt b/cookies.txt new file mode 100644 index 0000000..c31d989 --- /dev/null +++ b/cookies.txt @@ -0,0 +1,4 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + diff --git a/django_project/settings/base.py b/django_project/settings/base.py index 2b24682..5c78935 100644 --- a/django_project/settings/base.py +++ b/django_project/settings/base.py @@ -28,7 +28,6 @@ THIRD_PARTY_APPS = [ 'allauth.account', 'allauth.socialaccount', 'allauth.socialaccount.providers.google', - 'allauth.socialaccount.providers.facebook', 'whitenoise.runserver_nostatic', ] @@ -154,26 +153,6 @@ SOCIALACCOUNT_PROVIDERS = { 'access_type': 'online', }, 'OAUTH_PKCE_ENABLED': True, - }, - 'facebook': { - 'METHOD': 'oauth2', - 'SCOPE': ['email', 'public_profile'], - 'AUTH_PARAMS': {'auth_type': 'reauthenticate'}, - 'INIT_PARAMS': {'cookie': True}, - 'FIELDS': [ - 'id', - 'first_name', - 'last_name', - 'middle_name', - 'name', - 'name_format', - 'picture', - 'short_name' - ], - 'EXCHANGE_TOKEN': True, - 'LOCALE_FUNC': 'path.to.callable', - 'VERIFIED_EMAIL': False, - 'VERSION': 'v7.0', } } diff --git a/django_project/settings/development.py b/django_project/settings/development.py index f34272a..c3ea612 100644 --- a/django_project/settings/development.py +++ b/django_project/settings/development.py @@ -18,7 +18,7 @@ DATABASES = { } } -EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' +# EMAIL_BACKEND is configured in base.py from .env file LOGGING = { 'version': 1, diff --git a/entrypoint.sh b/entrypoint.sh index 47f84ff..12c75b7 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -11,6 +11,9 @@ python manage.py migrate --noinput echo "Creating default groups..." python manage.py create_groups +echo "Setting up social applications..." +python manage.py setup_social_apps + echo "Collecting static files..." python manage.py collectstatic --noinput diff --git a/static/css/auth.css b/static/css/auth.css new file mode 100644 index 0000000..2f8d8b1 --- /dev/null +++ b/static/css/auth.css @@ -0,0 +1,316 @@ +/* Modern Authentication Styles */ + +* { + box-sizing: border-box; +} + +body.auth-body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: #f8f9fa; + min-height: 100vh; + margin: 0; + padding: 0; + display: flex; + align-items: center; + justify-content: center; +} + +.auth-container { + width: 100%; + max-width: 600px; + padding: 20px; +} + +.auth-card { + background: #ffffff; + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + padding: 32px 48px; + width: 100%; + border: 1px solid #e5e7eb; +} + +.auth-logo { + width: 64px; + height: 64px; + background: #000000; + border-radius: 8px; + margin: 0 auto 24px; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; + font-weight: 600; + color: white; +} + +.auth-title { + font-size: 22px; + font-weight: 600; + color: #000000; + text-align: center; + margin-bottom: 6px; + line-height: 1.2; +} + +.auth-subtitle { + font-size: 14px; + color: #6b7280; + text-align: center; + margin-bottom: 24px; + font-weight: 400; +} + +.auth-form { + width: 100%; +} + +.form-group { + margin-bottom: 16px; +} + +.form-label { + font-size: 14px; + font-weight: 500; + color: #374151; + margin-bottom: 8px; + display: block; +} + +.form-control { + width: 100%; + padding: 10px 12px; + font-size: 14px; + border: 1.5px solid #d1d5db; + border-radius: 6px; + background: #ffffff; + transition: all 0.2s ease; + font-family: inherit; +} + +.form-control:focus { + outline: none; + border-color: #000000; + box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.1); + background: #ffffff; +} + +.form-control::placeholder { + color: #a0aec0; + font-weight: 400; +} + +.btn-primary { + width: 100%; + padding: 10px 16px; + font-size: 14px; + font-weight: 600; + background: #000000; + border: none; + border-radius: 6px; + color: white; + transition: all 0.2s ease; + margin-top: 8px; +} + +.btn-primary:hover { + transform: translateY(-1px); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2); + background: #1f2937; +} + +.btn-primary:active { + transform: translateY(0); +} + +.divider { + display: flex; + align-items: center; + margin: 20px 0; + color: #a0aec0; + font-size: 12px; + font-weight: 500; +} + +.divider::before, +.divider::after { + content: ''; + flex: 1; + height: 1px; + background: #d1d5db; +} + +.divider::before { + margin-right: 16px; +} + +.divider::after { + margin-left: 16px; +} + +.btn-google { + width: 100%; + padding: 10px 16px; + font-size: 14px; + font-weight: 500; + background: #ffffff; + border: 1.5px solid #d1d5db; + border-radius: 6px; + color: #374151; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + margin-bottom: 12px; + text-decoration: none; +} + +.btn-google:hover { + border-color: #9ca3af; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + background: #f9fafb; + color: #374151; + text-decoration: none; +} + + +.auth-links { + text-align: right; + margin: 0; +} + +.auth-links a { + color: #000000; + text-decoration: none; + font-weight: 500; + font-size: 12px; +} + +.auth-links a:hover { + color: #374151; + text-decoration: underline; +} + +.auth-links .separator { + color: #a0aec0; + margin: 0 8px; +} + +.auth-footer { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 16px; + font-size: 12px; + width: 100%; +} + +.forgot-password { + margin: 0; + text-align: left; +} + +.forgot-password a { + color: #6b7280; + text-decoration: none; + font-size: 12px; + font-weight: 500; +} + +.forgot-password a:hover { + color: #000000; + text-decoration: underline; +} + +.error-message { + background: #fee; + border: 1px solid #fcc; + color: #c66; + padding: 12px 16px; + border-radius: 8px; + margin-bottom: 20px; + font-size: 14px; +} + +.success-message { + background: #efe; + border: 1px solid #cfc; + color: #6c6; + padding: 12px 16px; + border-radius: 8px; + margin-bottom: 20px; + font-size: 14px; +} + +/* Responsive design */ +@media (max-width: 480px) { + .auth-card { + padding: 32px 24px; + margin: 20px; + border-radius: 12px; + } + + .auth-title { + font-size: 24px; + } + + .auth-subtitle { + font-size: 14px; + } +} + +/* Django form error styling */ +.errorlist { + list-style: none; + padding: 0; + margin: 8px 0 0 0; +} + +.errorlist li { + color: #e53e3e; + font-size: 14px; + font-weight: 500; +} + +.form-control.is-invalid { + border-color: #e53e3e; +} + +/* Hide default Django form styling */ +.form-row { + margin-bottom: 0; +} + +label { + font-size: 14px; + font-weight: 500; + color: #374151; + margin-bottom: 8px; + display: block; +} + +/* Social login not configured message */ +.social-not-configured { + text-align: center; + color: #a0aec0; + font-size: 14px; + margin-top: 16px; + font-style: italic; +} + +/* Toast customization */ +.toast { + border: none; + border-radius: 12px; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); +} + +.toast-header { + border-bottom: 1px solid #e2e8f0; + border-radius: 12px 12px 0 0; +} + +.toast-body { + font-weight: 500; +} \ No newline at end of file diff --git a/templates/account/dashboard.html b/templates/account/dashboard.html index 9be4d44..ea1885b 100644 --- a/templates/account/dashboard.html +++ b/templates/account/dashboard.html @@ -54,23 +54,33 @@
  • User Dashboard
  • - {% if user.has_role:'staff' or user.has_role:'admin' %} -
  • - - Staff Dashboard -
  • + {% if user.groups.all %} + {% for group in user.groups.all %} + {% if group.name == 'staff' or group.name == 'admin' %} +
  • + + Staff Dashboard +
  • + {% endif %} + {% if group.name == 'admin' %} +
  • + + Admin Dashboard +
  • + {% endif %} + {% endfor %} + {% if not user.groups.filter|length %} +
  • + Staff Dashboard +
  • +
  • + Admin Dashboard +
  • + {% endif %} {% else %}
  • Staff Dashboard
  • - {% endif %} - - {% if user.has_role:'admin' %} -
  • - - Admin Dashboard -
  • - {% else %}
  • Admin Dashboard
  • diff --git a/templates/account/email_confirm.html b/templates/account/email_confirm.html new file mode 100644 index 0000000..26c2410 --- /dev/null +++ b/templates/account/email_confirm.html @@ -0,0 +1,27 @@ +{% extends 'auth_base.html' %} + +{% block title %}Confirm Email{% endblock %} + +{% block content %} + + +

    Confirm Your Email

    +

    Click the button below to verify your email address

    + +{% if confirmation %} +
    + {% csrf_token %} + +
    +{% else %} +
    + Invalid or expired confirmation link. +
    +{% endif %} + + +{% endblock %} \ No newline at end of file diff --git a/templates/account/login.html b/templates/account/login.html index 63d2d28..39aa1ba 100644 --- a/templates/account/login.html +++ b/templates/account/login.html @@ -1,49 +1,105 @@ -{% extends 'base.html' %} +{% extends 'auth_base.html' %} {% load socialaccount %} -{% block title %}Login - Django Boilerplate{% endblock %} +{% block title %}Sign In{% endblock %} {% block content %} -
    -
    -
    -
    -

    Login to Your Account

    -
    -
    -
    - {% csrf_token %} - {{ form.as_p }} - -
    - -
    - -
    -
    Or login with:
    - -
    - -
    - -
    -

    - Don't have an account? - Sign up here -

    -

    - Forgot your password? -

    -
    + + +

    Welcome

    +

    Sign in to continue

    + +{% get_providers as socialaccount_providers %} +{% for provider in socialaccount_providers %} + {% if provider.id == "google" %} + + + + + + + + Continue with Google + + {% endif %} +{% endfor %} + +{% if socialaccount_providers %} +
    OR
    +{% endif %} + +
    + {% csrf_token %} + + {% if form.non_field_errors %} +
    + {% for error in form.non_field_errors %} + {{ error }} + {% endfor %} +
    + {% endif %} + +
    + + + {% if form.login.errors %} +
      + {% for error in form.login.errors %} +
    • {{ error }}
    • + {% endfor %} +
    + {% endif %} +
    + +
    + + + {% if form.password.errors %} +
      + {% for error in form.password.errors %} +
    • {{ error }}
    • + {% endfor %} +
    + {% endif %} +
    + + {% if form.remember %} +
    +
    + {{ form.remember }} +
    + {% endif %} + + +
    + + + +{% if not socialaccount_providers %} + +{% endif %} {% endblock %} \ No newline at end of file diff --git a/templates/account/password_reset.html b/templates/account/password_reset.html new file mode 100644 index 0000000..379a4a5 --- /dev/null +++ b/templates/account/password_reset.html @@ -0,0 +1,48 @@ +{% extends 'auth_base.html' %} + +{% block title %}Reset Password{% endblock %} + +{% block content %} + + +

    Reset Password

    +

    Enter your email to receive reset instructions

    + +
    + {% csrf_token %} + + {% if form.non_field_errors %} +
    + {% for error in form.non_field_errors %} + {{ error }} + {% endfor %} +
    + {% endif %} + +
    + + + {% if form.email.errors %} +
      + {% for error in form.email.errors %} +
    • {{ error }}
    • + {% endfor %} +
    + {% endif %} +
    + + +
    + + +{% endblock %} \ No newline at end of file diff --git a/templates/account/signup.html b/templates/account/signup.html index 9ed9577..899cfa4 100644 --- a/templates/account/signup.html +++ b/templates/account/signup.html @@ -1,46 +1,88 @@ -{% extends 'base.html' %} +{% extends 'auth_base.html' %} {% load socialaccount %} -{% block title %}Sign Up - Django Boilerplate{% endblock %} +{% block title %}Create Account{% endblock %} {% block content %} -
    -
    -
    -
    -

    Create Your Account

    -
    -
    -
    - {% csrf_token %} - {{ form.as_p }} - -
    - -
    - -
    -
    Or sign up with:
    - -
    - -
    - -
    -

    - Already have an account? - Login here -

    -
    -
    + + +

    Create Account

    +

    Sign up to get started

    + + +
    + {% csrf_token %} + + {% if form.non_field_errors %} +
    + {% for error in form.non_field_errors %} + {{ error }} + {% endfor %}
    + {% endif %} + +
    + + + {% if form.email.errors %} +
      + {% for error in form.email.errors %} +
    • {{ error }}
    • + {% endfor %} +
    + {% endif %} +
    + +
    + + + {% if form.password1.errors %} +
      + {% for error in form.password1.errors %} +
    • {{ error }}
    • + {% endfor %} +
    + {% endif %} +
    + +
    + + + {% if form.password2.errors %} +
      + {% for error in form.password2.errors %} +
    • {{ error }}
    • + {% endfor %} +
    + {% endif %} +
    + + +
    + + + {% endblock %} \ No newline at end of file diff --git a/templates/account/verification_sent.html b/templates/account/verification_sent.html new file mode 100644 index 0000000..22039ec --- /dev/null +++ b/templates/account/verification_sent.html @@ -0,0 +1,22 @@ +{% extends 'auth_base.html' %} + +{% block title %}Check Your Email{% endblock %} + +{% block content %} + + +

    Check Your Email

    +

    We've sent a verification link to your email address

    + +
    + Please check your email and click the verification link to complete your registration. +
    + + +{% endblock %} \ No newline at end of file diff --git a/templates/auth_base.html b/templates/auth_base.html new file mode 100644 index 0000000..6d9784e --- /dev/null +++ b/templates/auth_base.html @@ -0,0 +1,51 @@ + + + + + + {% block title %}Authentication{% endblock %} + {% load static %} + + + + + + +
    +
    + {% block content %} + {% endblock %} +
    +
    + + {% if messages %} +
    + {% for message in messages %} + + {% endfor %} +
    + {% endif %} + + + + + \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 0f46e08..ca87135 100644 --- a/templates/base.html +++ b/templates/base.html @@ -3,7 +3,7 @@ - {% block title %}Django Boilerplate{% endblock %} + {% block title %}Application{% endblock %} {% load static %} @@ -12,7 +12,7 @@