Support multiple authentication options (GitHub, Google, Username-only)#33
Support multiple authentication options (GitHub, Google, Username-only)#33
Conversation
…ion, hide admin nav - Add django-allauth for GitHub and Google OAuth authentication - Create custom RegistrationForm with optional email field and recovery warning - Update login/register templates with OAuth buttons and clear UI sections - Remove Django admin link from navigation (still accessible via /admin/ URL) - Update home page to link to course creation instead of admin - Configure allauth settings, backends, middleware in settings.py - Add OAuth credential placeholders to .env.example - Add 16 new tests covering auth forms, views, OAuth buttons, admin visibility Co-authored-by: coreysreid <111491611+coreysreid@users.noreply.github.com>
|
@copilot we had some CI tests fail can you sort this out |
…dependency The socialaccount providers (GitHub, Google OAuth) require the `requests` library which is only included when installing with the [socialaccount] extras specifier. Co-authored-by: coreysreid <111491611+coreysreid@users.noreply.github.com>
The CI failure was |
There was a problem hiding this comment.
Pull request overview
Adds multi-option authentication (GitHub OAuth, Google OAuth, and username/password with optional email) using django-allauth, updates the UI to surface OAuth entry points, and introduces tests covering the new auth UX and registration behavior.
Changes:
- Add and configure
django-allauth(apps, middleware, backends, provider config) and route/accounts/to allauth. - Introduce a custom
RegistrationFormwith optional email + warning messaging; update register flow to use it. - Update login/register templates to include OAuth buttons; hide the Django admin link from navigation; add tests for these behaviors.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
study_platform/urls.py |
Adds allauth URL include under /accounts/. |
study_platform/settings.py |
Enables allauth + providers and configures auth-related settings. |
study/views.py |
Switches registration to use the custom RegistrationForm. |
study/test_auth.py |
Adds test coverage for optional-email registration, OAuth button presence, and admin link hiding. |
study/templates/study/register.html |
Adds social sign-up buttons and an email-omission warning. |
study/templates/study/login.html |
Adds social sign-in buttons and keeps username/password login section. |
study/templates/study/home.html |
Replaces admin “Manage Content” CTA with “Create Course”. |
study/templates/study/base.html |
Removes Django admin link from the main navigation. |
study/forms.py |
Adds RegistrationForm with optional email + help text. |
requirements.txt |
Adds django-allauth[socialaccount] dependency. |
.env.example |
Adds OAuth credential placeholders for GitHub/Google. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
study_platform/settings.py
Outdated
| SOCIALACCOUNT_PROVIDERS = { | ||
| 'github': { | ||
| 'APP': { | ||
| 'client_id': os.getenv('GITHUB_OAUTH_CLIENT_ID', ''), | ||
| 'secret': os.getenv('GITHUB_OAUTH_SECRET', ''), | ||
| }, | ||
| 'SCOPE': ['read:user'], | ||
| }, | ||
| 'google': { | ||
| 'APP': { | ||
| 'client_id': os.getenv('GOOGLE_OAUTH_CLIENT_ID', ''), | ||
| 'secret': os.getenv('GOOGLE_OAUTH_SECRET', ''), | ||
| }, | ||
| 'SCOPE': ['profile'], | ||
| }, | ||
| } |
There was a problem hiding this comment.
GitHub OAuth is configured even when env vars are missing (defaulting to empty strings). If credentials are intentionally unset, this should be treated as “provider disabled” (e.g., don’t include the provider config / don’t render the button) to avoid a broken OAuth flow.
| SOCIALACCOUNT_PROVIDERS = { | |
| 'github': { | |
| 'APP': { | |
| 'client_id': os.getenv('GITHUB_OAUTH_CLIENT_ID', ''), | |
| 'secret': os.getenv('GITHUB_OAUTH_SECRET', ''), | |
| }, | |
| 'SCOPE': ['read:user'], | |
| }, | |
| 'google': { | |
| 'APP': { | |
| 'client_id': os.getenv('GOOGLE_OAUTH_CLIENT_ID', ''), | |
| 'secret': os.getenv('GOOGLE_OAUTH_SECRET', ''), | |
| }, | |
| 'SCOPE': ['profile'], | |
| }, | |
| } | |
| # Only configure social providers when credentials are present, | |
| # so that missing env vars effectively disable the provider. | |
| github_client_id = os.getenv('GITHUB_OAUTH_CLIENT_ID') | |
| github_secret = os.getenv('GITHUB_OAUTH_SECRET') | |
| google_client_id = os.getenv('GOOGLE_OAUTH_CLIENT_ID') | |
| google_secret = os.getenv('GOOGLE_OAUTH_SECRET') | |
| SOCIALACCOUNT_PROVIDERS = {} | |
| if github_client_id and github_secret: | |
| SOCIALACCOUNT_PROVIDERS['github'] = { | |
| 'APP': { | |
| 'client_id': github_client_id, | |
| 'secret': github_secret, | |
| }, | |
| 'SCOPE': ['read:user'], | |
| } | |
| if google_client_id and google_secret: | |
| SOCIALACCOUNT_PROVIDERS['google'] = { | |
| 'APP': { | |
| 'client_id': google_client_id, | |
| 'secret': google_secret, | |
| }, | |
| 'SCOPE': ['profile'], | |
| } |
study_platform/settings.py
Outdated
| SOCIALACCOUNT_PROVIDERS = { | ||
| 'github': { | ||
| 'APP': { | ||
| 'client_id': os.getenv('GITHUB_OAUTH_CLIENT_ID', ''), | ||
| 'secret': os.getenv('GITHUB_OAUTH_SECRET', ''), | ||
| }, | ||
| 'SCOPE': ['read:user'], | ||
| }, | ||
| 'google': { | ||
| 'APP': { | ||
| 'client_id': os.getenv('GOOGLE_OAUTH_CLIENT_ID', ''), | ||
| 'secret': os.getenv('GOOGLE_OAUTH_SECRET', ''), | ||
| }, | ||
| 'SCOPE': ['profile'], | ||
| }, | ||
| } |
There was a problem hiding this comment.
Google OAuth is configured even when env vars are missing (defaulting to empty strings). If credentials are intentionally unset, this should be treated as “provider disabled” (e.g., don’t include the provider config / don’t render the button) to avoid a broken OAuth flow.
| SOCIALACCOUNT_PROVIDERS = { | |
| 'github': { | |
| 'APP': { | |
| 'client_id': os.getenv('GITHUB_OAUTH_CLIENT_ID', ''), | |
| 'secret': os.getenv('GITHUB_OAUTH_SECRET', ''), | |
| }, | |
| 'SCOPE': ['read:user'], | |
| }, | |
| 'google': { | |
| 'APP': { | |
| 'client_id': os.getenv('GOOGLE_OAUTH_CLIENT_ID', ''), | |
| 'secret': os.getenv('GOOGLE_OAUTH_SECRET', ''), | |
| }, | |
| 'SCOPE': ['profile'], | |
| }, | |
| } | |
| # Read OAuth credentials without defaulting to empty strings so that | |
| # missing values can be treated as "provider disabled". | |
| GITHUB_OAUTH_CLIENT_ID = os.getenv('GITHUB_OAUTH_CLIENT_ID') | |
| GITHUB_OAUTH_SECRET = os.getenv('GITHUB_OAUTH_SECRET') | |
| GOOGLE_OAUTH_CLIENT_ID = os.getenv('GOOGLE_OAUTH_CLIENT_ID') | |
| GOOGLE_OAUTH_SECRET = os.getenv('GOOGLE_OAUTH_SECRET') | |
| SOCIALACCOUNT_PROVIDERS = {} | |
| if GITHUB_OAUTH_CLIENT_ID and GITHUB_OAUTH_SECRET: | |
| SOCIALACCOUNT_PROVIDERS['github'] = { | |
| 'APP': { | |
| 'client_id': GITHUB_OAUTH_CLIENT_ID, | |
| 'secret': GITHUB_OAUTH_SECRET, | |
| }, | |
| 'SCOPE': ['read:user'], | |
| } | |
| if GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_SECRET: | |
| SOCIALACCOUNT_PROVIDERS['google'] = { | |
| 'APP': { | |
| 'client_id': GOOGLE_OAUTH_CLIENT_ID, | |
| 'secret': GOOGLE_OAUTH_SECRET, | |
| }, | |
| 'SCOPE': ['profile'], | |
| } |
| <a href="{% provider_login_url 'github' %}" class="btn" style="display: flex; align-items: center; justify-content: center; gap: 0.5rem; background: #24292e; color: white; text-decoration: none; padding: 0.75rem; border-radius: 5px;"> | ||
| <svg height="20" width="20" viewBox="0 0 16 16" fill="white"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg> | ||
| Sign in with GitHub | ||
| </a> | ||
| <a href="{% provider_login_url 'google' %}" class="btn" style="display: flex; align-items: center; justify-content: center; gap: 0.5rem; background: #4285f4; color: white; text-decoration: none; padding: 0.75rem; border-radius: 5px;"> |
There was a problem hiding this comment.
These OAuth buttons are rendered unconditionally. If a provider is not configured/enabled, the link should be hidden (or replaced with disabled UI) to avoid sending users into a failing OAuth flow.
| @@ -21,6 +21,7 @@ | |||
|
|
|||
| urlpatterns = [ | |||
| path('admin/', admin.site.urls), | |||
There was a problem hiding this comment.
The root URLConf mounts Django admin at the /admin/ prefix, but the study app also defines routes under /admin/feedback/... (admin_feedback_review/update_feedback_status). Requests to /admin/feedback/... will be captured by admin.site.urls first, making those study routes unreachable. Consider moving the study feedback URLs out of the /admin/ prefix (e.g., /staff/feedback/...) or mounting Django admin under a different prefix (e.g., /django-admin/) and updating links/tests accordingly.
| path('admin/', admin.site.urls), | |
| path('django-admin/', admin.site.urls), |
| <a href="{% provider_login_url 'github' %}" class="btn" style="display: flex; align-items: center; justify-content: center; gap: 0.5rem; background: #24292e; color: white; text-decoration: none; padding: 0.75rem; border-radius: 5px;"> | ||
| <svg height="20" width="20" viewBox="0 0 16 16" fill="white"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg> | ||
| Sign up with GitHub | ||
| </a> | ||
| <a href="{% provider_login_url 'google' %}" class="btn" style="display: flex; align-items: center; justify-content: center; gap: 0.5rem; background: #4285f4; color: white; text-decoration: none; padding: 0.75rem; border-radius: 5px;"> |
There was a problem hiding this comment.
These OAuth sign-up buttons are rendered unconditionally. If a provider is not configured/enabled, the link should be hidden (or replaced with disabled UI) to avoid sending users into a failing OAuth flow.
.env.example
Outdated
| ALLOWED_HOSTS=localhost,127.0.0.1 | ||
| GITHUB_REPO=owner/repo-name | ||
|
|
||
| # OAuth providers (optional - leave empty to disable) |
There was a problem hiding this comment.
This comment says OAuth credentials can be left empty to “disable” providers, but the app still renders provider buttons and configures the providers regardless. Either remove/adjust this guidance, or implement conditional provider enablement + conditional button rendering when credentials are present.
| # OAuth providers (optional - leave empty to disable) | |
| # OAuth providers (optional - set these only if you plan to use them) |
|
@copilot apply changes based on the comments in this thread |
- Only configure SOCIALACCOUNT_PROVIDERS when env vars are present - Add oauth_providers context processor for template conditionals - Conditionally render OAuth buttons on login/register pages - Move feedback URLs from admin/feedback/ to staff/feedback/ to fix Django admin catch-all routing conflict - Update .env.example comment - Update tests: conditional OAuth visibility, feedback URL routing Co-authored-by: coreysreid <111491611+coreysreid@users.noreply.github.com>
Applied all 6 review comments in
|

Add flexible authentication via GitHub OAuth, Google OAuth, or traditional username/password with optional email. Hide Django admin from app navigation.
Changes
django-allauth[socialaccount]with GitHub and Google social providers. Credentials configured via env vars (GITHUB_OAUTH_CLIENT_ID,GOOGLE_OAUTH_CLIENT_ID, etc.)RegistrationFormmakes email optional with clear warning about password recovery implications/admin/)admin/feedback/...tostaff/feedback/...to avoid conflict with Django admin's catch-all URL handlerConfiguration
OAuth providers are only enabled when both
client_idandsecretenv vars are present. Missing or empty values disable the provider and hide its button:Screenshots
Login page (no OAuth configured):

Login page (OAuth configured):

Register page (OAuth configured):

Security Summary: CodeQL scan found 0 alerts. No vulnerabilities introduced.
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.