I've got most of this reset password flow done using djoser in django. I get the email successfully. The last part where I redirect back to Vue.js frontend after the user follows the link in the email is the only thing I'm missing. For clarity I expect to be redirected to my Vue frontent url with uid
and token
present. From there I can finish this off. My django files are:
backend/settings.py (look at DJOSER setup):
"""
Django settings for backend project.
Generated by 'django-admin startproject' using Django 4.2.1.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
from pathlib import Path
from django.urls import reverse
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
BASE_URL = 'http://127.0.0.1:8080'
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-1z1=&mfysp%pklgh@x+^_j3+p5)x+zm@va36wc9eq0z=7r3u7z'
STRIPE_SECRET_KEY = 'sk_test_51NH4LnIIOIhiNEiGlcpsukLE5pi6Me9zYOhTf1uVP4ux7WgCXyR5QG9b6Yzfgm6qB4Agl8ZcQwZ7RN2rKIex04nk00kF97Aeok'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['127.0.0.1']
CORS_ALLOW_CREDENTIALS = True
CSRF_TRUSTED_ORIGINS = [
'http://127.0.0.1:8080',
]
CORS_ALLOWED_ORIGINS = [
'http://127.0.0.1:8080',
]
CSRF_COOKIE_HTTPONLY = False
# SESSION_COOKIE_SAMESITE = 'None'
# CORS_ALLOW_CREDENTIALS = True
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = BASE_DIR / 'emails'
# Application definition
DJOSER = {
'PASSWORD_RESET_CONFIRM_URL': 'api/v1/password/reset/confirm/{uid}/{token}', #help me change this please
# 'ACTIVATION_URL': '#/api/v1/activate/{uid}/{token}',
'SEND_ACTIVATION_EMAIL': True,
'SERIALIZERS': {},
}
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework.authtoken',
'corsheaders',
'djoser',
'product',
'order',
'email_app'
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'backend.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'backend.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
STATIC_URL = 'static/'
MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / 'media/'
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
backend/urls.py:
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('djoser.urls')),
path('api/v1/', include('djoser.urls.authtoken')),
path('api/v1/', include('product.urls')),
path('api/v1/', include('order.urls')),
path('api/v1/', include('email_app.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
in the frontend I have routes for ResetPassword, ResetPasswordDone and ResetPasswordConfirm.
frontend/router/index.js:
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import store from '../store'
import ResetPassword from '../views/ResetPassword.vue'
import ResetPasswordConfirm from '../views/ResetPasswordConfirm.vue'
import ResetPasswordDone from '../views/ResetPasswordDone.vue'
const routes = [
{
path: '/reset-password',
name: 'ResetPassword',
component: ResetPassword
},
{
path: '/reset-password-done',
name: 'ResetPasswordDone',
component: ResetPasswordDone
},
{
path: '/api/v1/password/reset/confirm/:{uid}/:{token}', // this is the path I want DJOSER to redirect to with `uid` and `token`
name: 'ResetPasswordConfirm',
component: ResetPasswordConfirm
},
]
Steps I take regarding DJOSER:
api/v1/reset_password
const config = {
headers:{
'Content-Type': 'application/json'
}
}
const email = this.email;
const body = JSON.stringify({ email })
axios.defaults.withCredentials = true;
axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRFToken' : this.getCookie('csrftoken')
};
axios.post('/api/v1/users/reset_password/', body, config)
backend/emails:
Content-Type: multipart/alternative;
boundary="===============0720596674918646286=="
MIME-Version: 1.0
Subject: Password reset on 127.0.0.1:8000
From: webmaster@localhost
To: kenneth.shimabukuro@gmail.com
Date: Fri, 21 Jul 2023 11:01:05 -0000
Message-ID: <168993726590.34747.17316762060486301205@2.1.168.192.in-addr.arpa>
--===============0720596674918646286==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
You're receiving this email because you requested a password reset for your user account at 127.0.0.1:8000.
Please go to the following page and choose a new password:
http://127.0.0.1:8000/api/v1/password/reset/confirm/NQ/brop9t-929c0b1e6031f0d34ca241f1867ba0c1
Your username, in case you've forgotten: ken4
Thanks for using our site!
The 127.0.0.1:8000 team
All I need is djoser to send an email redirecting to http:127.0.0.1:8080
not http:127.0.0.1:8000
with the uid
and token
present. How do I do that?
Feel like I'm talking to myself here but here's the answer (perhaps I asked badly).
Add DOMAIN
in settings.py. This is based on the django docs and this answer that specify if your backend and frontend are on different ports then DOMAIN
needs to be specified
Updated settings.py:
...
DOMAIN = '127.0.0.1:8080'
SITE_NAME = 'd-commerce'
...
DJOSER = {
'PASSWORD_RESET_CONFIRM_URL': 'api/v1/users/reset_password_confirm/{uid}/{token}',
'PASSWORD_RESET_CONFIRM_RETYPE' : True,
'SERIALIZERS': {},
}
...
From there you'll receive an email
Content-Type: multipart/alternative;
boundary="===============5014392667940436431=="
MIME-Version: 1.0
Subject: Password reset on d-commerce
From: webmaster@localhost
To: a@a.com
Date: Mon, 24 Jul 2023 13:33:30 -0000
Message-ID: <169020561045.43044.9860348640042926888@2.1.168.192.in-addr.arpa>
--===============5014392667940436431==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
You're receiving this email because you requested a password reset for your user account at d-commerce.
Please go to the following page and choose a new password:
http://127.0.0.1:8080/api/v1/users/reset_password_confirm/MTI/brugbu-c956f64f925f359d044a85c405aff712
Your username, in case you've forgotten: t1
Thanks for using our site!
The d-commerce team
--===============5014392667940436431==
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
The frontend functionality was easy to figure out from there. Hope this helps somebody else.