JWT Token created is not matching decoded in Flask - flask

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)

Related

How to extract payload from a jwt Expired 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!

Flask_restx #api.route return statement results in a TypeError

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

How can I use JWT token in DRF tests?

I need to test my API. For example, I have images list page. I need to do tests for this page, but I cannot do this without authentication. I use JWT. I do not know how to do it.
Help me please.
tests.py
class ImagesListTestCase(APITestCase):
def test_images_list(self):
response = self.client.get('/api/', HTTP_AUTHORIZATION="JWT {}".format("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTkzNzUxMzMxLCJqdGkiOiI3NWE3NDNkMGU3MDQ0MGNiYjQ3NDExNjQ3MTI5NWVjNSIsInVzZXJfaWQiOjF9.7HkhZ1hRV8OtQJMMLEAVwUnJ0yDt8agFadAsJztFb6A"))
self.assertEqual(response.status_code, status.HTTP_200_OK)
I tried to do
response = self.client.get('/api/', Authorization="Bearer <token>")
Also
response = self.client.get('/api/', Authorization="JWT <token>")
response = self.client.get('/api/', HTTP_AUTHORIZATION="Bearer <token>")
django creates a temporary database for test; so it's better to get token from username and password like this:
class ImagesListTestCase(APITestCase):
def setUp(self) :
self.register_url = reverse("your:register:view") # for example : "users:register"
self.user_data = {
"username": "test_user",
"email": "test_user#gmail.com",
"password": "123456"
}
self.client.post(self.register_url,self.user_data) # user created
auth_url = reverse("your:login:view") #for example :"users:token_obtain_pair"
self.access_token = self.client.post(auth_url,{
"username" : self.user_data.get("username") ,
"password" : self.user_data.get("password")
}).data.get("access") # get access_token for authorization
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
def test_images_list(self):
response = self.client.get('/api/')
self.assertEqual(response.status_code, status.HTTP_200_OK)
You can create users that require JWT authentication like:
def setUp(self) -> None:
self.client = APIClient()
if os.environ.get('GITHUB_WORKFLOW'):
local_token_url = 'https://testing/app/token/jwt'
else:
local_token_url = 'http://127.0.0.1:8000/app/token/jwt'
response = requests.post(local_token_url, {'email': 'test_contact1#user.com', 'password': '123pass321'})
self.test_user1 = json.loads(response.text)
response = requests.post(local_token_url, {'email': 'test_contact2#user.com', 'password': '123pass321'})
self.test_user2 = json.loads(response.text)
self.contact_person = apm.AppUser.objects.create(email="test_contact1#user.com", first_name="John",
last_name="Doe",
company_name="test_company_1", phone_number="08077745673",
is_active=True)
you can then use them like below parsing data = {}
self.client.credentials(HTTP_AUTHORIZATION="Bearer {}".format(self.test_user1.get('access')))
response = self.client.post(reverse('create-list'), data=data, format='json')
print(response.data)
self.assertEqual(response.status_code, status.HTTP_201_CREATE
Instead of invoking api to get the token, Another very simple solution can as follows
from rest_framework_simplejwt.tokens import AccessToken
def setUp(self):
self.factory = APIRequestFactory()
user = User.objects.create_user(
username='jacob', email='jacob#gmail.com', password='top_secret')
self.client = APIClient()
token = AccessToken.for_user(user=user)
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}')
and it will be used as
def test_get_cities(self):
response = self.client.get('/<actual path to url>/cities/')
assert response.status_code == 200
assert len(response.data) == 0
self.assertEqual(response.data, [])
Important thing to notice here is in setUp function token is generated using AcessToken class from JWT itself, so no actual API is required for testing other flows in your app.

Authorization VK API error: u'{"error":"invalid_request","error_description":"Security Error"}'

I'm trying to authorize from my djano-app on vk.com. I'm using requests and client authorization. I'm trying to authorize by this way and getting an error:
{"error":"invalid_request","error_description":"Security Error"}
Internet suggests to re-login on VK in browser, but there isn't any solution for authorization from code.
My code:
class VkApiSingleton(object):
api_version = '5.95'
def __init__(self,
app_id=config.APP_ID,
login=config.ACCOUNT_LOGIN,
pswd=config.ACCOUNT_PASSWORD,
permissions='video,offline,groups'
):
# type: (int, str, str, str) -> None
self.app_id = app_id
self.app_user_login = login
self.app_user_pass = pswd
self.access_token = None
self.user_id = None
self.session = requests.Session()
self.form_parser = FormParser()
self.permissions = permissions
def __new__(cls, *args, **kwargs):
if not hasattr(cls, 'instance'):
cls.instance = super(VkApiSingleton, cls).__new__(cls, *args, **kwargs)
return cls.instance
#property
def get_session(self):
return self.session
def _parse_form(self, response):
# type: (requests.models.Response) -> None
self.form_parser = FormParser()
try:
self.form_parser.feed(str(response.content))
except Exception as err:
logger.exception(
'Error checking HTML form',
extra={'Error body': str(err)}
)
def _submit_form(self, **kwargs):
# type: (dict) -> requests.models.Response
if self.form_parser.method == 'post':
payload = copy.deepcopy(self.form_parser.params)
if kwargs.get('is_login', False):
payload.update({
'email': self.app_user_login,
'pass': self.app_user_pass
})
with self.get_session as session:
try:
return session.post(self.form_parser.url, data=payload)
except Exception as err:
logger.exception(
'Error submitting auth form',
extra={'Error body': str(err)}
)
raise VkApiError('Error submitting auth form: %s' % str(err))
def _log_in(self):
# type: () -> requests.models.Response
response = self._submit_form(is_login=True)
self._parse_form(response)
if response.status_code != 200:
raise VkApiError('Auth error: cant parse HTML form')
if 'pass' in response.text:
logger.error(
'Wrong login or password'
)
raise VkApiError('Wrong login or password')
return response
def _submit_permissions(self, url=None):
# type: () -> requests.models.Response
if 'submit_allow_access' in self.form_parser.params and 'grant_access' in self.form_parser.url:
return self._submit_form(token_url=url)
else:
logger.warning(
'Cant submit permissions for application'
)
def _get_token(self, response):
# type: (requests.models.Response) -> None
try:
params = response.url.split('#')[1].split('&')
self.access_token = params[0].split('=')[1]
self.user_id = params[2].split('=')[1]
except IndexError as err:
logger.error(
'Cant get access_token',
extra={'Error body': str(err)}
)
def auth(self):
auth_url = 'https://oauth.vk.com/authorize?revoke=1'
redirect_uri = 'https://oauth.vk.com/blank.html'
display = 'wap'
request_params = {
'client_id': self.app_id,
'scope': self.permissions,
'redirect_uri': redirect_uri,
'display': display,
'response_type': 'token',
'v': self.api_version
}
with self.get_session as session:
response = session.get(
auth_url,
params=request_params
)
self._parse_form(response)
if not self.form_parser.form_parsed:
raise VkApiError('Invalid HTML form. Check auth_url or params')
else:
login_response = self._log_in()
permissions_response = self._submit_permissions()
self._get_token(login_response)
If someone has a similar problem - I found some reasons of this.
1) Invalid type of authorization - try to use another type of authorization (it describes in official documentation)
2) Too many authorizations.
I solved problem like this:
1) Get token with "offline" permission by "Client Application Authorization"
2) Every time I need to use vk.api method - I am checking my token for expiring with secure method "secure.checkToken" (you need to get Service token to use this method. There are a lot of information in official documentation)
3) If my token expires - i am getting the new one.

django rest framwork Error decoding signature with jwt RS256

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.