what is the best way to generate a reset token in python? - python

I'm trying to make a validation process for a password reset, what i've used are two values: the epoch time, and i want to use the users's old password (pbkdf2) as a key,
Since i dont want to get non ASCII characters, i've used SimpleEncode library because it's fast since it's only a BASE64 with a key used, but the problem is that the password is too long (196 chars) so i get a long key!
What i've done is split the result code = simpleencode.encode(key,asci)[::30], but this will not be unique!
To get an idea how it works, i've tried Facebook reset process, but what is given is a number! so how this process works, don't they use a key to make it hard for someone to forge a link to reset someone's password?
Update: how the algorithme will work:
1- get the time using epoche time.time()
2- generate the Base64 of the epoche time (to use for the URL) and the epoch time value + a key, this key is PBKDF2(password).
3- generate the url www.example.com/reset/user/Base64(time.time()) and send this URL + the simpleencode.encode(key,asci)[::30]
4- when the user clicks on the URL, he put the generated code, this generated code, if it matches with the URL, then let him modifiy the password, else, it is a forget URL!

Not sure it's the best way, but I'd probably just generate a UUID4, which can be used in a URL to reset the password and expire it after 'n' amount of time.
>>> import uuid
>>> uuid.uuid4().hex
'8c05904f0051419283d1024fc5ce1a59'
You could use something like http://redis.io to hold that key, with a value of the appropriate user ID and set its time to live. So, when something comes in from http://example.com/password-reset/8c05904f0051419283d1024fc5ce1a59 it looks to see if it's valid and if so then allows changes to set a new password.
If you did want a "validation pin", then store along with the token, a small random key, eg:
>>> from string import digits
>>> from random import choice
>>> ''.join(choice(digits) for i in xrange(4))
'2545'
And request that be entered on the reset link.

Easiest way by far is to use the ItsDangerous library:
You can serialize and sign a user ID for unsubscribing of newsletters into URLs. This way you don’t need to generate one-time tokens and store them in the database. Same thing with any kind of activation link for accounts and similar things.
You can also embed a timestamp, so very easily to set time periods without having to involve databases or queues. It's all cryptographically signed, so you can easily see if it's been tampered with.
>>> from itsdangerous import TimestampSigner
>>> s = TimestampSigner('secret-key')
>>> string = s.sign('foo')
>>> s.unsign(string, max_age=5)
Traceback (most recent call last):
...
itsdangerous.SignatureExpired: Signature age 15 > 5 seconds

Why not just use a jwt as token for this purpose, its also possible to set an expiration time to it, so its also possible to put an expiration date to the token.
Generate token(JWT) encrypted with a secret key
Send mail containg a link with the token as a query paramater(When the user opens the link the page can read the token)
Verify the token before saving the new password
For generating jwt tokens I use pyjwt. The below code snippet shows how it can be done with an expiry time of 24 hours(1 day) and signed with a secret key:
import jwt
from datetime import datetime, timedelta, timezone
secret = "jwt_secret"
payload = {"exp": datetime.now(timezone.utc) + timedelta(days=1), "id": user_id}
token = jwt.encode(payload, secret, algorithm="HS256")
reset_token = token.decode("utf-8")
Below snippet shows how the verification of token and the new password can be set in django. If the token has expired or has been tampered with, it will raise an exception.
secret = "jwt_secret"
claims = jwt.decode(token, secret, options={"require_exp": True})
# Check if the user exists
user = User.objects.get(id=claims.get("id"))
user.set_password(password)
user.save()

Related

Verify Metamask signature (ethereum) using Python

I would like to verify an ethereum (ETH) signature made in MetaMask using python. I'm developing a website using flask as backend.
Javascript code send a POST requests to the back end containing the 3 following variables:
{'signature': '0x0293cc0d4eb416ca95349b7e63dc9d1c9a7aab4865b5cd6d6f2c36fb1dce12d34a05039aedf0bc64931a439def451bcf313abbcc72e9172f7fd51ecca30b41dd1b', 'nonce': '6875972781', 'adress': '0x3a806c439805d6e0fdd88a4c98682f86a7111789'}
My goal is to verify that the signature contains the nonce (random integer) and was sign by the public adress
I using javascript to sign the nonce using the ether library
const ethereum = window.ethereum;
const provider = new ethers.providers.Web3Provider(ethereum)
const signer = provider.getSigner()
var signature = await signer.signMessage(nonce);
I tried with several python libraires, but I'm unable to format signature, adress and nonce so that it works. here is unsuccessfull try made using ecdsa librairy:
vk = ecdsa.VerifyingKey.from_string(bytes.fromhex(address), curve=ecdsa.SECP256k1, hashfunc=sha256)
vk.verify(bytes.fromhex(hex(signature)), bytes(nonce, 'utf-8'))
I get the following error:
ValueError: non-hexadecimal number found in fromhex() arg at position 1
Thanks for your help !
Using web3.py you could use w3.eth.account.recover_message to recover the address from the signature and the data. After that you compare the adress to the correct adress(with lowercase, because i think web3.py would give you lower and uppercase)
from web3 import Web3
from hexbytes import HexBytes
from eth_account.messages import encode_defunct
w3 = Web3(Web3.HTTPProvider(""))
mesage= encode_defunct(text="6875972781")
address = w3.eth.account.recover_message(mesage,signature=HexBytes("0x0293cc0d4eb416ca95349b7e63dc9d1c9a7aab4865b5cd6d6f2c36fb1dce12d34a05039aedf0bc64931a439def451bcf313abbcc72e9172f7fd51ecca30b41dd1b"))
print(address)
You can verify using JS but I commend you for wanting to verify on the backend with python. I was faced with the exact same choice and opted for the latter. Basically, I created 2 apis, the first of which generates the message and nonce and the second verifies the signature.
I'm using FastAPI for this but you can pattern it to any framework you prefer such as Django.
Generate message:
# ethaccount is the user's wallet included in the body of the request
async def generate_message(ethaccount: str = Body(...)) -> str:
# I save the wallet to cache for reference later
set_cache_address(ethaccount)
# Generate the nonce
nonce = uuid.uuid4()
# Generate the message
message = f'''
Welcome! Sign this message to login to the site. This doesn't cost you
anything and is free of any gas fees.
Nonce:
{nonce}.
'''
return message
Metamask takes over from here. Afterwards verify the signature generated by metamask:
from web3.auto import w3
from eth_account.messages import encode_defunct
async def signature(data: dict = Body(...)): # noqa
# User's signature from metamask passed through the body
sig = data.get('signature')
# The juicy bits. Here I try to verify the signature they sent.
message = encode_defunct(text=data.get('message'))
signed_address = (w3.eth.account.recover_message(message, signature=sig)).lower()
# Same wallet address means same user. I use the cached address here.
if get_cache_address() == signed_address:
# Do what you will
# You can generate the JSON access and refresh tokens here
pass
NOTE: I've cleaned this code to show only the logic/methods you might need. The actual code I'm using is longer as I generate tokens, etc.
JackDonMClovin, Body is a request auto parse that is provided by fastapi. For other libs you can get the signature data from the request object.

How to make a token expire after a while? (without jwt)

I have a simple login system that I did with flask.
After logging in, I return to the user a type of token with which the user can send messages like chat at another route.
I would like to expire that token after some time (a minute for example), the token will expire and the user will have to log in again to get a new token.
So my question is, how do you get the token erased or something like that after expiry time?
After the user log in, I save the login time in a dict like this:
login_time[datetime.datetime.now()] = data['username']
and then I thought of doing something like this before or in the chat route:
for time, user in login_time.items():
if datetime.datetime.now() >= time + datetime.timedelta(seconds=30):
del login_time[time]
But I dont know where should I put it and how it will work.
this is a part of the chat route:
#app.route('/chat', methods=['POST'])
def chat():
try:
data = request.get_json()
username = verify_token(data['token']) # Verifying if token is in tokens list
validate_message(data['message']) # Verifying if message is not empty
chats.append(username + ": " + data['message'])
for i, u in login_time.items(): # Not sure about this part
if i != u:
abort(400, 'Your token is expired, please login again')
return jsonify(chats)
One easy to implement solution, if you do not want to use JWT, is :
When you generate your token, generate the expiration date at the same time
Log both in a database
When the user tries to post a message, its token is sent with the message
check if the token is still valid (either you check if datetime.now() > token_expiration_time or you can even have a script delete every expired token from the database every x minuts and then just check if the token is found in the database or not)
If the token is expired you redirect the user and ask him to log in again to generate another token.
when using a token based auth, you'll have to identify and verify the access by that token, i.e identify the client (which user is this) and verify check the token validity like expiration
you can incorporate this data in the token itself (JWT)
let's say the token is smth like 'token-1571153241', where the number is unix timestamp of expiration
1) parse the expiration timestamp from the token
2) if expiration reached return expiry message, else serve the request
* keep in mind to issue the token with the expiry date
or
use any db to store the token's information
1) parse the token from the request
2) check the db for the token validity
3) pass or reject based the criteria
method 1 is better performance wise, no access to external source (db) is required, but less control of the token attribute, e,g very hard to implement a manual expiration of the token (or change the user privileges access) manually

How can I get the encrypted password in the auth-manager with PyQGIS?

I'm making a QGIS plugin in which I ask the for authentication object (with QgsProcessingParameterAuthConfig) for the PostgreSQL connection (which is already set in the connecions list of the user).
My goal is to take the login and password with PyQGIS and use these to connect with psycopg2.
The asked parameter QgsProcessingParameterAuthConfig returns a string with the identification key of the authentication object.
I can get the QgsAuthMethodConfig object with this key but the
password is empty.
I didn't found a method to access the password, nor other plugin doing that.
It is possible to know the SQLite database where the password is saved, but the are encrypted and I don't know the method to decrypt them.
So it seems like you do the following with the string (id):
# get the config id as a string
auth_method_id = self.parameterAsString(
parameters,
self.AUTH_CONFIG,
context
)
# get the application's authenticaion manager
auth_mgr = QgsApplication.authManager()
# create an empty authmethodconfig object
auth_cfg = QgsAuthMethodConfig()
# load config from manager to the new config instance and decrypt sensitive data
auth_mgr.loadAuthenticationConfig(auth_method_id, auth_cfg, True)
# get the configuration information (including username and password)
auth_cfg.configMap()
I got this from various places in documentation:
https://qgis.org/pyqgis/master/core/QgsAuthManager.html
https://qgis.org/pyqgis/master/core/QgsAuthMethodConfig.html
https://qgis.org/pyqgis/master/core/QgsProcessingParameterAuthConfig.html
Minor addition to isamson's helpful response above:
You will need to add QgsApplication, QgsAuthManager, QgsAuthMethodConfig to your from qgis.core import call, if not already there.
You will want to assign that last object to a variable, which you can then pull credentials out of:
auth_info = auth_cfg.configMap()
try:
user = auth_info['username']
password = auth_info['password']

How can I get crypto keys via SL api as in portal?

I'm trying to get the cryptographic keys just like portal, but I can't make my mask work, can someone tell me what's wrong with the following request, btw I am using the following url
https://sldn.softlayer.com/reference/services/softlayer_security_certificate_request/getsslcertificaterequests
and jic python api client is used and also rest requests can work for me to get the socket layers.
mask = "mask[accountId, certificateSigningRequest, certificateAuthorityName, id]
response = client['SoftLayer_Security_Certificate_Request'].getsslcertificaterequests()
I also want to find how to search the virtual ips associated to the certificates but I don't find what api method does what i need.
Your current code will retrieve only one of the certificates and not the stored security certificates, in order to make your mask work you need to add close the string with " double quote, and the method you are calling should be getSslCertificateRequests, see below:
accountId = 202768 #change this value
mask = "mask[accountId, certificateSigningRequest, certificateAuthorityName, id]"
response = client['SoftLayer_Security_Certificate_Request'].getSslCertificateRequests(accountId)
Currently the portal use SoftLayer_Account::getSecurityCertificate to retrieve the stored security certificates including SSL, use the following Python script:
import SoftLayer
from pprint import pprint as pp
USERNAME = 'set-me'
# Endpoint url that contains all the Api Services.
API_KEY = 'set-me'
# Generate one for you or your users, or view yours at
https://control.softlayer.com/account/users
client = SoftLayer.create_client_from_env(username=USERNAME,
api_key=API_KEY)
accountService = client['SoftLayer_Account']
try:
""""
getSecurityCertificates() retrieves stored security certificates (ie. SSL)
"""""
result = accountService.getSecurityCertificates()
pp(result)
except SoftLayer.SoftLayerAPIError as e:
""""
If there was an error returned from the SoftLayer API then bomb out with the
error message.
"""""
print("Unable to retrieve the Account's stored security certificates (i.e. SSL) . %s %s " % (e.faultCode, e.faultString))
To find the virtual ip addresses associated you should use the method getAdcLoadBalancers and send the id value obtained in the previous method, try this Rest request.
https://[username]:[apiKey]#api.softlayer.com/rest/v3.1/SoftLayer_Account/getAdcLoadBalancers?objectFilter={"adcLoadBalancers":{"securityCertificateId":{"operation":[id]}}}
Remember to change the username and apiKey for valid credentials, and the id mentioned above to retrieve the associated load balancer ip addresses.

Facebook Access Token for Automatic Script (get token every time script runs)

I'm trying to set up a script that will run every day at 7am and pull our organization's ad data from Facebook. I got the actual Python script totally working:
(I used my actual app id, app secret, account, and user access token instead of X's of course, and wound up storing it in a csv but didn't copy that part of the code because it isn't relevant)
from facebookads.objects import AdAccount
from facebookads.api import FacebookAdsApi
import pandas as pd
my_app_id = 'XXX'
my_app_secret = 'XXX'
my_access_token = 'XXX'
FacebookAdsApi.init(my_app_id, my_app_secret, my_access_token)
account = AdAccount('act_XXX')
params = {
'data_columns':['adgroup_id','date_start','date_stop','campaign_name','reach', 'frequency', 'impressions', 'clicks', 'unique_clicks', 'ctr', 'unique_ctr', 'spend', 'cpm','cpp', 'cpc', 'actions'],
'date_preset': 'last_90_days',
}
stats = account.get_report_stats(params=params);
print (stats)
The problem is that this token expires every 60 minutes. I need a way that I won't have to modify this program every day to get authenticated in Facebook. I realize that the above token is the USER token. I've tried the App Token (which is "app_id|app_secret") and I've even gotten Python to give it to me through a request:
import requests
r = requests.get("https://graph.facebook.com/oauth/access_token? client_id={{my_id}}&client_secret={{my_secret}}&grant_type=client_credentials")
#print r.headers
token = r.content
print (token)
But it won't take this as the actual token. It only gives me the ad data when I manually put in the USER token. If I'm understanding the situation correctly, I need either
1) A permanent token (Don't think this exists)
or 2) a way to request the (user?) token from the Facebook API every time the Python script runs.
This is Facebook's page on access tokens (where I got the code from) https://developers.facebook.com/docs/facebook-login/access-tokens I know there is the extended 60 day token but that won't work because then we still have to manually go in and change it every 60 days. The idea is to have the script run automatically and not have to re-code it for each run.
Thanks in advance.

Categories

Resources