I'm using django rest framwork with jwt.
In setting I used RS256 ALGORITHM
I want send token after user authentication, this is my function I'm trying to send token with user_id and is_user data it will produce token but when I pass token in request to server server response:
detail
:
"Error decoding signature."
Why?
here is my http://localhost:8000/login/ server response:
{
"username": "admin",
"email": "",
"token": "b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJpc191c2VyIjp0cnVlfQ.kIz9TBYqVJVgFV5siM3QfNWHxBN28BlZRY_t8TFADmg'",
"is_staff": false
}
login func:
JWT_SECRET = 'secret'
JWT_ALGORITHM = 'HS256'
JWT_EXP_DELTA_SECONDS = 20
def validate(self, data):
user_obj = None
email = data.get('email', None)
username = data.get('username', None)
password = data.get('password')
if not email and not username:
raise ValidationError("email or username is required!")
if '#' in username:
email = username
user = User.objects.filter(
Q(email=email) |
Q(username=username)
).distinct()
# user = user.exclude(email__isnull=True).exclude(email__iexact='')
if user.exists() and user.count() == 1:
user_obj = user.first()
else:
raise ValidationError("this username/email is not valid")
if user_obj:
if not user_obj.check_password(password):
raise ValidationError("password is incorrect")
# payload_handler(user)
# payload = payload_handler(user_obj)
payload = {
'user_id': user_obj.id,
'is_user': True,
}
jwt_token = jwt.encode(payload, JWT_SECRET, JWT_ALGORITHM)
# code = json_response({'token': jwt_token.decode('utf-8')})
data['token'] = jwt_token
return data
jwt setting:
JWT_AUTH = {
'JWT_SECRET_KEY': SECRET_KEY,
'JWT_ALGORITHM': 'RS256',
'JWT_AUTH_HEADER_PREFIX': 'Bearer',
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=6600),
}
For your "logic func" you use HS256 algorithm and in your "jwt setting" you use RS256. I think it must be a problem.
Related
I`m receiving 401 in my JWT requests to the method get user infos from token (), but the tokens doesnt seem to be the same.
I already made the following checks: 1. Checked the token string is exactly the same both in the localStorage and in the create_access_token result. 2. The secret key is the same both in the token creation and in the token decoding 3. The algorithm used for encoding is also the same as the one used for decoding
What else can I do to fix it?
app = Flask(__name__)
app.config.from_object(__name__)
app.config['JWT_SECRET_KEY'] = 'example-key'
#app.route('/login', methods=['POST'])
def login():
# Extract the username and password from the request
user_email = request.get_json(silent=True).get('user_email', None)
user_password = request.get_json(silent=True).get('user_password', None)
# Validate the credentials using the UserController
user = UserController().find_user_by_user_email_and_password(user_email, user_password)
if user is False:
return jsonify({'login': False}), 401
# Calculate the expiration time
exp = datetime.utcnow() + timedelta(days=7)
# Create a JWT token with the user identity and expiration time
access_token = create_access_token(identity=user['user_id'], expires_delta=timedelta(days=7))
print("Token: '" + str(access_token) + "'")
# Return the JWT token in the response
return jsonify({'access_token': access_token}), 200
#app.route('/get_user_infos_from_token', methods=['GET'])
def get_user_infos_from_token():
try:
authorization_header = request.headers.get('Authorization')
token = authorization_header.split(" ")[1]
print("Token: '" + str(token) + "'")
decoded_token = jwt.decode(token, app.config['JWT_SECRET_KEY'], algorithms=['HS256'])
user_id = decoded_token['identity']
user = UserController().find_user_by_user_id(user_id)
if user is None:
return jsonify({'error': 'User not found'}), 404
return jsonify(user), 200
except:
return jsonify({'error': 'Invalid token'}), 401
the method below I'm using to authenticate the user, generate the token and insert in localStorage:
async login(){
let self = this;
axios.post('http://127.0.0.1:5000/login', {
user_email: self.email
, user_password: self.password
})
.then(function (response) {
localStorage.setItem('token', response.data.access_token);
console.log(response.data)
self.login_error = '';
window.alert('Uhu, deu certo! Vou te redirecionar para a página principal.');
self.$router.push({path: '/'});
})
.catch(function (error) {
self.login_error = 'Usuário ou senha incorretos. Tente novamente.';
console.log(error);
});
},
and the method below I am using to get the infos from the user, based on the token that is in the localStorage, that was created during the authentication.
mounted(){
if (localStorage.getItem('token') != null == true){
let self = this;
const token = localStorage.getItem('token').toString();
console.log(token);
axios.get('http://localhost:5000/get_user_infos_from_token', {
headers: { Authorization: `Bearer ${token}` }
}).then(response => {
self.user_data = response.data;
}).catch(error => {
console.error(error);
});
localStorage.setItem('user_data', self.user_data);
self.user_auth_flag = true;
}
},
What do I need to do to make sure the jwt decoder will use the same secret key and algorithm to decode the token generated during the authentication?
..
you did not post your jwt create and validate function. I have a jwt_helper class, you can use it for your own purpose:
PyJWT==1.4.2
import jwt
import os
class JWTHelper:
#staticmethod
def gen_auth_token(identity, email) -> str:
return JWTHelper.encode_auth_token({
"id": identity,
"email": email,
"expired": Helper.get_now_unixtimestamp() + 60 * 60, # expire in 1h,
})
#staticmethod
def encode_auth_token(payload) -> str:
secret_key = os.environ["JWT_SECRET"] # your secret
print(secret_key)
"""
Generates the Auth Token
:return String
"""
try:
return jwt.encode(
payload,
secret_key,
algorithm='HS256'
).decode('utf-8')
except Exception as e:
print(e)
#staticmethod
def decode_auth_token(token):
secret_key = os.environ["JWT_SECRET"]
"""
Decode the auth token
"""
try:
payload = jwt.decode(token, secret_key, algorithms='HS256')
return payload
except Exception as e:
print(e)
raise Unauthorized(message="Please log in again.")
# return None
#staticmethod
def validate_token(token: str):
data = JWTHelper.decode_auth_token(token)
print(data)
if not data:
raise Unauthorized(message='invalid token')
expired = data.get("expired") or None
user_id = data. Get("id") or None
if expired is None or user_id is None:
raise Unauthorized(message='invalid token')
if expired < Helper.get_now_unixtimestamp():
raise Unauthorized(message='token expired')
use your class
token = JWTHelper.gen_auth_token(
identity=1,
email="tuandao864#gmail.com",
role_id=1,
)
validate = JWTHelper.validate_token(auth_token)
I have made a view where I send a Refresh Token to email for activation account purpose. If token is valid everything works fine. The problem is when jwt token expire, I want to be able in backend to extract payload (user_id) from token when jwt.decode throw ExpiredSignatureError and in this way be able to auto resend an Email based on user_id extracted from token.
Here is how I generate the token:
def activation_link(request, user, email):
token = RefreshToken.for_user(user)
curent_site = "localhost:3000"
relative_link="/auth/confirm-email"
link = 'http://' + curent_site + relative_link + "/" + str(token)
html_message = render_to_string('users/email_templates/activate_account.html',{
'activation_link': link,
})
text_content = strip_tags(html_message)
email_subject = 'Activate your account'
from_email = 'notsure#yahoo.com'
to_email = email
#api_view(['POST'])
def ConfirmEmailView(request):
try:
activation_token = request.data['activation_token']
payload = jwt.decode(activation_token,settings.SECRET_KEY, algorithms=['HS256'])
user = User.objects.get(id = payload['user_id'])
if user.is_confirmed:
return Response('Already verified!', status=status.HTTP_200_OK)
user.is_confirmed = True
user.save()
return Response(status=status.HTTP_202_ACCEPTED)
except jwt.ExpiredSignatureError as identifier:
// =>>> Here I want to decode activation_token and extract user_id
return Response("Link- expired!", status=status.HTTP_403_FORBIDDEN)
except Exception as e:
print(e)
return Response(status=status.HTTP_400_BAD_REQUEST)
Well, apparently the solution is easy:
def ConfirmEmailView(request):
try:
activation_token = request.data['activation_token']
payload = jwt.decode(activation_token,settings.SECRET_KEY, algorithms=['HS256'])
user = User.objects.get(id = payload['user_id'])
if user.is_confirmed:
return Response('Already verified!', status=status.HTTP_200_OK)
user.is_confirmed = True
user.save()
return Response(status=status.HTTP_202_ACCEPTED)
except jwt.ExpiredSignatureError as identifier:
# Here we are:
payload = jwt.decode(request.data['activation_token'],settings.SECRET_KEY, algorithms=['HS256'],options={"verify_signature": False})
user_id = payload['user_id'];
return Response({'user_id':user_id, status=status.HTTP_2OO_OK})
By adding options={"verify_signature": False} is posible to decode the token just fine!
I have a problem I am not able to solve. I want to make use of http cookies in flask. This is the code from documentation:
#app.route('/token/auth', methods=['POST'])
def login():
username = request.json.get('username', None)
password = request.json.get('password', None)
if username != 'test' or password != 'test':
return jsonify({'login': False}), 401
# Create the tokens we will be sending back to the user
access_token = create_access_token(identity=username)
refresh_token = create_refresh_token(identity=username)
# Set the JWT cookies in the response
resp = jsonify({'login': True})
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp, 200
I use flask_restx which automatically turns the response into JSON, so that jsonify in the first example is not needed. However, still still need to jsonify it, because i can not use set_access_cookie on a dictionary. This results at the end in a nested response like this jsonify(jsonify(x))
#api.route("/login")
class UserLogin(Resource):
def post(self):
"""Allows a new user to login with his email and password"""
email = request.get_json()["email"]
password = request.get_json()["password"]
user = User.query.filter_by(email=email).one_or_none()
if user is None:
return {"message": "user does not exist"}, 404
user = user.format()
if bcrypt.check_password_hash(pw_hash=user["password"], password=password):
if user["active"]:
resp = jsonify({"login": True})
access_token = create_access_token(identity=user)
refresh_token = create_refresh_token(user)
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp, 200
# return (
# {"access_token": access_token, "refresh_token": refresh_token},
# 200,
# )
else:
return {"message": "User not activated"}, 400
else:
return {"message": "Wrong credentials"}, 401
This is the error: TypeError: Object of type Response is not JSON serializable
Any ideas how can I overcome this?
Was able to solve it like this:
data = dict(login=True)
resp = make_response(jsonify(**data), 200)
access_token = create_access_token(identity=user)
refresh_token = create_refresh_token(user)
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp
I am implementing a custom JSONWebTokenSerializer. It's working fine so far but I need to enable token refresh but I when I do and try to refresh the token, I get the validation error orig_iat field is required. On inspecting the payload returned from jwt_payload_handler, there isn't any orig_iat field attribute.
class CustomJWTSerializer(JSONWebTokenSerializer):
#property
def username_field(self):
return "username_or_email_or_phone"
def validate(self, attrs):
username = attrs.get('username_or_email_or_phone', None)
credentials = {
'username': username,
'password': attrs.get('password')
}
if all(credentials.values()):
user = authenticate(**credentials)
if user:
if not user.is_active:
raise serializers.ValidationError(
'This user has been deactivated.'
)
payload = jwt_payload_handler(user)
return {
'token': jwt_encode_handler(payload),
'user': user
}
else:
raise serializers.ValidationError(
'A user with this credentials was not found.'
)
else:
msg = _('Please provide an (username or email or phone number) and password.')
raise serializers.ValidationError(msg)
Here is are my JWT_AUTH setting:
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_RESPONSE_PAYLOAD_HANDLER': 'common.utilities.auth.jwt_response_payload_handler',
'JWT_AUTH_HEADER_PREFIX': 'Bearer',
'JWT_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_payload_handler',
'JWT_ALLOW_REFRESH': False,
}
If JWT_ALLOW_REFRESH is True, non-expired tokens can be "refreshed" to obtain a brand new token with renewed expiration time.[JWTDoc]
Sorry, found my bug. Changed 'JWT_ALLOW_REFRESH': False to 'JWT_ALLOW_REFRESH': True,.. It now works
I am using Django - ldap authentication in my project . Once if the user is authenticated , i need to set a cookie and return as a response to the server .
def post(self,request):
userData = json.loads(request.body)
username = userData.get('username')
password = userData.get('password')
oLdap = LDAPBackend()
if username == "admin" and password == "admin":
User_Grps = "AdminLogin"
else:
try:
User = oLdap.authenticate(username=username,password=password)
if User is not None:
User_Grps = User.ldap_user.group_dns
else:
User_Grps = "Check your username and password"
except ldap.LDAPError:
User_Grps = "Error"
return HttpResponse(User_Grps)
How to add a cookie to the response and send it with a the User_Grps to the client
response = HttpResponse(User_Grps)
response.set_cookie(key, value)
return response
That's it.