Skip to content

Commit

Permalink
Implement refresh token endpoint for JWT management
Browse files Browse the repository at this point in the history
  • Loading branch information
trisDeveloper committed Oct 30, 2024
1 parent 3fe0fa9 commit 5fc2419
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 32 deletions.
10 changes: 8 additions & 2 deletions backend/focusty/focusty/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
from pathlib import Path
from decouple import config, Csv
from datetime import timedelta

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
Expand All @@ -28,8 +29,8 @@
"focusty_app",
]

CORS_ORIGIN_ALLOW_ALL = True

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = config("CORS_ALLOWED_ORIGINS", cast=Csv())
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
"django.middleware.security.SecurityMiddleware",
Expand Down Expand Up @@ -128,3 +129,8 @@
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
}

SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
"REFRESH_TOKEN_LIFETIME": timedelta(days=60),
}
2 changes: 2 additions & 0 deletions backend/focusty/focusty_app/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# urls.py
from django.urls import path
from rest_framework_simplejwt.views import TokenRefreshView
from .views import (
UserList,
UserDetail,
Expand All @@ -20,6 +21,7 @@
path("users/<int:user_id>/tasks/all/", tasks_count, name="tasks count"),
path("login/", login_view, name="login"),
path("register/", RegisterView.as_view(), name="register"),
path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
path("users/<int:user_id>/pomodoros/", PomodoroListCreate.as_view()),
path("users/<int:user_id>/pomodoros/<int:pk>/", PomodoroDetail.as_view()),
]
19 changes: 7 additions & 12 deletions backend/focusty/focusty_app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
class UserList(generics.ListCreateAPIView):
queryset = User.objects.filter(is_staff=False)
serializer_class = UserSerializer
# permission_classes = [IsAdminUser]
permission_classes = [IsAdminUser]


class UserDetail(generics.RetrieveUpdateDestroyAPIView):
Expand Down Expand Up @@ -49,16 +49,11 @@ def create(self, request, *args, **kwargs):
# Hash the password securely
user.set_password(user.password)
user.save()
token = self.get_token(user)
response.set_cookie(key="jwt", value=str(token), httponly=True)
response.data["token"] = str(token)
token = RefreshToken.for_user(user)
response.data["access"] = str(token.access_token)
response.data["refresh"] = str(token)
return response

def get_token(self, user):

refresh = RefreshToken.for_user(user)
return refresh.access_token

def get_user_from_response(self, data):
try:
user_id = data.get("id")
Expand All @@ -83,7 +78,7 @@ def login_view(request):

if user.check_password(password):
# Generate token
refresh = RefreshToken.for_user(user)
token = RefreshToken.for_user(user)

return JsonResponse(
{
Expand All @@ -93,8 +88,8 @@ def login_view(request):
"username": user.username,
"email": user.email,
},
"access": str(refresh.access_token),
"refresh": str(refresh),
"access": str(token.access_token),
"refresh": str(token),
}
)
else:
Expand Down
40 changes: 31 additions & 9 deletions frontend/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,41 @@ import 'v-calendar/style.css'
//axios
axios.defaults.baseURL = import.meta.env.VITE_BASE_URL
axios.defaults.headers['Content-Type'] = 'application/json'
axios.interceptors.request.use(
(config) => {
const token = Cookies.get('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
axios.defaults.withCredentials = true
axios.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config
const access = Cookies.get('access')
if (access) {
axios.defaults.headers['Authorization'] = `Bearer ${access}`
return axios(originalRequest)
} else if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true

const refresh = Cookies.get('refresh')
if (refresh) {
const res = await axios.post('/api/token/refresh/', { refresh })
const newAccessToken = res.data.access
Cookies.set('access', newAccessToken, {
expires: 60 / 1440,
secure: true,
sameSite: 'Strict'
})
axios.defaults.headers['Authorization'] = `Bearer ${newAccessToken}`
return axios(originalRequest)
} else {
localStorage.clear()
Cookies.remove('access')
Cookies.remove('refresh')
router.push('/')

window.location.href = '/'
}
}
return config
},
(error) => {
return Promise.reject(error)
}
)

// font awesome icons
import { library } from '@fortawesome/fontawesome-svg-core'
import {
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/views/user/log-in.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ const login = async () => {
})
// Handle successful login
localStorage.setItem('userId', response.data.user.id)
const [token, refresh] = [response.data.access, response.data.refresh]
if (token) {
Cookies.set('token', token, { expires: 7, secure: true, sameSite: 'Strict' })
Cookies.set('refresh', refresh, { expires: 7, secure: true, sameSite: 'Strict' })
const [access, refresh] = [response.data.access, response.data.refresh]
if (access && refresh) {
Cookies.set('access', access, { expires: 60 / 1440, secure: true, sameSite: 'Strict' })
Cookies.set('refresh', refresh, { expires: 60, secure: true, sameSite: 'Strict' })
}
store.setUser({
id: response.data.user.id,
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/views/user/sign-up.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ const signup = async () => {
})
// Handle successful signup
localStorage.setItem('userId', response.data.id)
const token = response.data.token
if (token) {
Cookies.set('token', token, { expires: 7, secure: true, sameSite: 'Strict' })
const [access, refresh] = [response.data.access, response.data.refresh]
if (access && refresh) {
Cookies.set('access', access, { expires: 60 / 1440, secure: true, sameSite: 'Strict' })
Cookies.set('refresh', refresh, { expires: 60, secure: true, sameSite: 'Strict' })
}
store.setUser({
id: response.data.id,
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/views/user/user-profile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ const deleteAccount = () => {
.then(() => {
//window.reload()
localStorage.clear()
Cookies.remove('token')
Cookies.remove('access')
Cookies.remove('refresh')
store.setUser(null)
router.push('/')
window.location.href = '/focusty/'
Expand All @@ -88,7 +89,8 @@ const logout = () => {
if (confirm('Are you sure you want to log out?')) {
store.setUser(null)
localStorage.clear()
Cookies.remove('token')
Cookies.remove('access')
Cookies.remove('refresh')
router.push('/')
window.location.href = '/'
Expand Down

0 comments on commit 5fc2419

Please sign in to comment.