the newly registered user get email for activation . He clicks on the links and move to an activation page .then he clicks on verify button which take uid and token from the link and post it to auth/users/activation/ and then gets the response stale token for the given user no matter how fast he click on the link on verify link . result is same. I am using djoser for activation and all user related stuff.and redux in the frontend for api calls and also the React as frontend
here is my settings.py:
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=30),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'AUTH_HEADER_TYPES': ('JWT','Bearer'),
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule' ,
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
}
DJOSER = {
'LOGIN_FIELD': 'email',
'USERNAME_CHANGED_EMAIL_CONFIRMATION': True,
'PASSWORD_CHANGED_EMAIL_CONFIRMATION': True,
'SEND_CONFIRMATION_EMAIL': True,
'SET_USERNAME_RETYPE': True,
'SET_PASSWORD_RETYPE': True,
'PASSWORD_CHANGED_EMAIL_CONFIRMATION':True,
'PASSWORD_RESET_CONFIRM_URL': 'password/reset/confirm/{uid}/{token}',
'SEND_ACTIVATION_EMAIL': True,
'ACTIVATION_URL': 'activate/{uid}/{token}',
'SOCIAL_AUTH_TOKEN_STRATEGY': 'djoser.social.token.jwt.TokenStrategy',
'SOCIAL_AUTH_ALLOWED_REDIRECT_URIS': ['https://negoziohub.herokuapp.com/google', 'http://127.0.0.1:8000/facebook'],
'SERIALIZERS': {
'user_create': 'base.serializers.UserSerializer',
'user': 'base.serializers.UserSerializer',
'user_delete': 'djoser.serializers.UserDeleteSerializer',
}
}
here is userAction.js:
export const activate = (uid, token) => async (dispatch) => {
try {
dispatch({
type: USER_ACTIVATE_REQUEST
})
const config = {
headers: {
'Content-type': 'application/json',
}
}
const body = JSON.stringify({ uid, token });
const { data } = await axios.post(`/auth/users/activation/`, body,
config
)
dispatch({
type: USER_ACTIVATE_SUCCESS,
payload: data
})
// dispatch(login())
localStorage.setItem('userInfo', JSON.stringify(data))
} catch (error) {
dispatch({
type: USER_ACTIVATE_FAIL,
payload: error.response && error.response.data.detail
? error.response.data.detail
: error.message,
})
}
}
Any help would be appreciated. Thanks❤❤❤
I know it's too late to answer your question but I will do it just for other folks out there who might face the same issue in the future.
I had the same problem and I couldn't find anything on the internet, which drove me to dive deep in the source code of Djoser. The issue in my case was that I changed the default behavior to change the state of the user and set user.is_active = True. This was the issue as Djoser sends the stale token error if user.is_active != False.
Hope this helps.
Related
I am authenticating user with http only cookies which contains jwt tokens.
Heres all my settigns require to use cookies.
settings.py
CORS_EXPOSE_HEADERS = ["Content-Type", "X-CSRFToken"]
CORS_ALLOW_CREDENTIALS = True
# CORS_ALLOW_HEADERS = ["Set-Cookie",]
SESSION_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_HTTPONLY = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = "None"
CSRF_COOKIE_SECURE = True
CSRF_TRUSTED_ORIGINS = ["http://localhost:3000", "http://127.0.0.1:3000"]
'ACCESS_TOKEN_LIFETIME': timedelta(days=2),
'REFRESH_TOKEN_LIFETIME': timedelta(days=10),
# HTTPOnly Cookies
"AUTH_COOKIE": "access_token",
"AUTH_COOKIE_REFRESH": "refresh_token",
"AUTH_COOKIE_SAMESITE": "None",
"AUTH_COOKIE_SECURE": True,
"AUTH_COOKIE_HTTP_ONLY": True,
"AUTH_COOKIE_PATH": "/",
"AUTH_COOKIE_DOMAIN": None,
axios.js
import axios from "axios";
import { BASE_URL_BACK_SERVER } from "./_variables";
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.xsrfHeaderName = "X-CSRFToken";
axios.defaults.withCredentials = true;
export const axiosInstanceBack = axios.create({
baseURL: BASE_URL_BACK_SERVER,
timeout: 5000,
headers: {
"Content-Type": "application/json",
},
});
Authenticating user working fine but the problem is when logging out user.
when it makes request for LogoutView it return 200 response.
But after redirecting in frontend again fetches current user which should be available if only user is logged in. When checked to network tab i found that request header for current user's details contain same cookies. Why is that happening? am i missing something here?
LogoutView
class LogoutView(APIView):
def post(self, request):
response = Response()
response.delete_cookie(settings.SIMPLE_JWT['AUTH_COOKIE'])
response.delete_cookie(settings.SIMPLE_JWT['AUTH_COOKIE_REFRESH'])
response.data = {
"status": "success",
"msg": "User logout successfully"
}
return response
logout Request
const logoutUser = () => {
axiosInstanceBack
.post("user/logout/", {}, { headers: { "X-CSRFToken": csrfToken } })
.then((response) => {
mutateCsrf();
setShouldFetch(false);
mutate("user/current/", null, { revalidate: false });
router.push("/login");
})
.catch((error) => console.log(error));
};
I have my backend in Django and front in Vue.
A user performes login in Vue and via a POST request the creds are sent to a Django JWT login endpoint. This endpoint returns a token which is set in localStorage.
Then I want to check in Vue that the user is logged in. For that another endpoint in Django exists. However, it always returns "AnonymUser". I cannot get how to set this check.
Django:
My settings.py
JWT_AUTH = {
'JWT_ALLOW_REFRESH': True,
'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=1),
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
My urls.py
path('check-auth', views.check_if_logged_in, name="check-auth"), # check auth
path('auth/obtain_token', obtain_jwt_token), # obtain token
path('auth/refresh_token', refresh_jwt_token),
My views.py
# Login Check
#csrf_exempt
def check_if_logged_in(request):
authentication_class = (JSONWebTokenAuthentication,)
permission_classes = (IsAuthenticated,)
print(request.user) # returns AnonymUser
check = None
if request.user.is_authenticated:
check = True
else:
check = False
print(check) # returns False
return HttpResponse(f"<html><body>{check}</body></html>")
Vue
obtainToken function
obtainToken(){
var that = this;
fetch(this.endpoints.obtainJWT, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: that.django.username,
password: that.django.password
})
}).then(response => response.json()
).then(function(response) {
console.log('auth', response); # get token
that.updateToken(response.token); # update localStorage
that.checkAuthData(); #check auth
});
},
checkAuth function
checkAuthData: function() {
var that = this;
fetch('http://localhost:8000/check-auth', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
token: this.jwt # send token
})
}).then(response => response.json()
).then(function(response) {
console.log('check', response);
});
},
You should include token not in the body, but in the header instead:
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.jwt
},
Also, please make sure that in your Django settings in REST_FRAMEWORK DEFAULT_AUTHENTICATION_CLASSES contains JWT authentication:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
...
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
}
I am trying to make a form that edits the user's profile data and uses the patch method to change all the fields except the username because the username is the primary key and only identifier of a specific user.
Update:
-I changed it to a regular PATCH method and the data does not update
-I also made sure formData sends the correct data, which it does
From reading previous questions, I've learned that a normal axios.patch method does not work with FormData and I've followed the instructions of adding:
formData.append("_method", "PATCH")
and changing the axios method to axios.put, however, I still receive a 400 error which states, in the data section:
username: ["This field is required."]
Help would be much appreciated. Thank you, kind strangers!
Edit Profile Submit:
onSubmit = (e) => {
e.preventDefault();
const { firstName, lastName, profile } = this.state;
const username = localStorage.getItem("username");
formData.append("_method", "PATCH");
formData.append("firstName", firstName);
formData.append("lastName", lastName);
formData.append("profile_picture", profile);
this.props.onUpdate(formData, username);
};
Axios Request
export const userUpdate = (formData, username) => {
axios
.post(`http://127.0.0.1:8000/user/api/${username}`, {
formData,
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error.response);
});
};
User models.py
class User(models.Model):
firstName = models.CharField(max_length = 20, blank = True, default='Anonymous')
lastName = models.CharField(max_length = 25, blank = True, default='')
username = models.CharField(max_length = 50, unique=True, primary_key=True)
profile_picture = models.ForeignKey('upload.Profile', on_delete=models.CASCADE, null=True)
Profile Picture models.py
class Profile(models.Model):
image = models.ImageField(blank=False, null=False, upload_to=profile_path, default='f_profile.jpg')
You are supposed to be using PATCH if you want a patch request.
export const userUpdate = (formData, username) => {
axios
.patch(`http://127.0.0.1:8000/user/api/${username}`, {
formData,
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error.response);
});
};
Also, drop the _method from the form. Unless you have a specific view to make something of it, you won't need it.
I am working on password reset and I have used django rest-auth, I have successfully got the token and uid from email link by hitting rest-auth/password/reset/,
but for to confirm I want the token and uid in react form so I can change it how can I get the uid and token which the rest auth return in email link in react js form
axios.post('http://127.0.0.1:8000/rest-auth/password/reset/',payload)
.then(res=>{
console.log(res)
})
.catch(err=>{
console.log(err)
})
its working perfect and it returns me:
http://127.0.0.1:8000/rest-auth/password/reset/confirm/MQ/594-5faaa46be4277e6a1879/
how can I get the uid and token from url in react form?
You should configure your react router to get the params from url.
e.preventDefault();
fetch("http://127.0.0.1:8000/rest-auth/password/reset/confirm/", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}, body: JSON.stringify({
"new_password1": this.state.new_password1,
"new_password2": this.state.new_password2,
"uid": this.props.match.params.uid,
"token": this.props.match.params.token,
})
})
// .then(response => response.json())
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})
A have an Ember (v2.12.0-beta.1) app that uses ember-simple-auth-token to request a JWT.
The important part happens in the login controller.
export default Ember.Controller.extend({
session: Ember.inject.service(),
// Properties
username: 'user1',
password: 'password123',
// Actions
actions: {
login(username, password) {
console.log('Attempting login...');
let creds = this.getProperties('username', 'password');
let authenticator = 'authenticator:jwt';
this.get('session').authenticate(authenticator, creds).then(function() {
console.log('LOGIN SUCCESS')
}, function() {
console.log('LOGIN FAIL')
});
}
}
});
When submitting the form, there is a request that is being made by the browser and my backend receives it.
The problem is that only the password is included in the request. The body of the request has the form {"password":"password123"}, but it should look like {"username":"user1","password":"password123"}. Of course, the login attempt fails and LOGIN FAIL is printed.
Why is the username not included in the token request?
I tried using earlier versions of ember-simple-auth-token and ember-simple-auth.
Here is my configuration:
ENV['ember-simple-auth'] = {
authorizer: 'authorizer:token',
};
ENV['ember-simple-auth-token'] = {
serverTokenEndpoint: 'http://127.0.0.1:6003/token',
identificationField: 'username',
passwordField: 'password',
tokenPropertyName: 'token',
authorizationPrefix: 'Bearer ',
authorizationHeaderName: 'Authorization',
refreshAccessTokens: false,
};
ember-simple-auth-token expects credentials object passed to authenticate to be in format:
{
identification: <username>,
password: <password>
}
So your code should look something like this:
actions: {
login(username, password) {
console.log('Attempting login...');
let creds = {
identification: username,
password: password
};
let authenticator = 'authenticator:jwt';
this.get('session').authenticate(authenticator, creds).then(function() {
console.log('LOGIN SUCCESS')
}, function() {
console.log('LOGIN FAIL')
});
}
}
The request sent in this case is:
{
"password":"password123",
"username":"user1"
}
There are some pull requests about this issue.