how can repeat signup aws cognito [solve] - python-3.x

solve is here gist
when sign up in our app, sign up with aws cognito and send verify email with code.
if user close app not input verify, user have to re sign up. (saved in cognito userpool state UNCONFIRM)
and it occur two problem.
password may be changed (when re sign up)
renew verify code
my code is here python3 and warrant
#app.route('/signup/', methods=['POST'])
def signup():
u = Cognito(os.getenv('COGNITO_USER_POOL_ID'), os.getenv('COGNITO_CLIENT_ID'),
user_pool_region=os.getenv('COGNITO_REGION'))
u.add_base_attributes(name=user_name, email=user_email)
u.register(user_email, user_password)
return redirect(url_for('lobby'))
err code
botocore.errorfactory.UsernameExistsException: An error occurred (UsernameExistsException) when calling the SignUp operation: An account with the given email already exists.
how to re signup with renew password, and send new verify email
Thanks

oh, i solve
#app.route('/signup/', methods=['POST'])
def signup():
idp_client = boto3.client('cognito-idp')
'''
resp = idp_client.sign_up(ClientId=app_client_id,
Username=user_email,
Password=user_password,
UserAttributes=[{'Name': 'email', 'Value': user_email}])
'''
resp = idp_client.resend_confirmation_code(ClientId=os.getenv('COGNITO_CLIENT_ID'),
Username=user_email)
print(resp) #check result
return redirect(url_for('lobby'))
use boto3 i want (resend not acquire password)

Related

Keycloak API :- Identify Users Realm for login

I am creating a microservice that will proxy keycloak for user creation, reset password, login etc. I don't want to expose any keycloak page like re-set password or login page so I am using the keycloak API and everything is fine so far.
The only issue is for login, where I need to know the realm to get the token as the API to get the token is realm specific.
realms/{REALM_NAME}/protocol/openid-connect/token
So is there a way to get a list of all the users from all the realms by admin user?
or any other way to find it?
You can get the realm information by decode user's access token.
The "iss" (issuer) claim identifies the principal that issued the JWT.
This is decode example by JWT.io
I demo make two realms (realm1 and realm2)
each realm add single user (both user same username: user and password: 1234)
And call get access token and decode it by Python
import requests
import ast
import jwt
def get_issuer(realm, user_name, password):
url = 'http://localhost:8180/auth/realms/'+realm+'/protocol/openid-connect/token'
body = {
'client_id': 'admin-cli',
'grant_type': 'password',
'username' : user_name,
'password': password
}
headers = {
'content-type': 'application/x-www-form-urlencoded'
}
response = requests.post(url, data=body, headers=headers).content.decode('utf-8')
token = ast.literal_eval(response)['access_token']
# print(token)
decoded = jwt.decode(token, options={"verify_signature": False})
# print(decoded)
return decoded['iss']
print('realm1 with user -->', get_issuer('realm1','user','1234'))
print('realm2 with user -->', get_issuer('realm2','user','1234'))
get this output
$python get_realm.py
realm1 with user --> http://localhost:8180/auth/realms/realm1
realm2 with user --> http://localhost:8180/auth/realms/realm2
If you want to get all users of realm,
you can get this API with master realm's admin token
GET /{realm}/users
After talking with No_One, I realize he want to get a relam name by username.
I made 600 realms and 3 users for each realm. So total user 1.8K and each user unique username. (If may same username, we can extend username and e-mail). I made python program to create relams and users.
So I demo search realm by username with for loop.
Check username exist for every realm
For loop all of relams
{keycloak_URL}/auth/admin/realms/{realm_name}/users/?username={user_name}
if you want to get list of realms,
{keycloak-url}/auth/admin/realms
The realm name format is realm_0xxx
example) realm_0001, realm_0002, ..., realm_0600
each ream has three users
example) In realm_0001,
user01_in_realm0001,
user02_in_realm0001,
user03_in_realm0001
In realm_0002,
user01_in_realm0002,
user02_in_realm0002,
user03_in_realm0002
...
In realm_0600,
user01_in_realm0600,
user02_in_realm0600,
user03_in_realm0600
This Python code search user by for loop
import admin
import random
admin = admin.Admin()
token = admin.get_master_token()
random_realm_num = random.randint(1, 600)
random_user_num = random.randint(1, 3)
realm_name = "realm_{:04d}".format(random_realm_num)
user_name = "user{:02d}_in_realm{:04d}".format(random_user_num, random_realm_num)
print('random realm_name:', realm_name)
print('random user_name:', user_name)
found = False
for realm_index in range(1,600,1):
realm_name = "realm_{:04d}".format(realm_index)
if(admin.is_user_exist(token, realm_name, user_name)):
print('user_name:', user_name,' belong to',realm_name)
found = True
break
if (not found):
print('user_name:', user_name,'is not belong to any realms')
This admin class
from urllib import response
from urllib.error import HTTPError
import requests
import ast
import json
class Admin:
# Keycloak master realm URL
url = 'http://localhost:8180/auth/realms/master/protocol/openid-connect/token'
# Keycloak master credential
params = {
'client_id': 'admin-cli',
'grant_type': 'password',
'username' : 'admin',
'password': 'admin'
}
def get_master_token(self):
try:
response = requests.post(self.url, self.params, verify=False).content.decode('utf-8')
except HTTPError as http_err:
print(f'HTTP error occurred: {http_err}') # Python 3.6
except Exception as err:
print(f'Other error occurred: {err}') # Python 3.6
print('Keycloak container is not running, Please check your docker container!')
raise SystemExit
else:
return ast.literal_eval(response)['access_token']
def is_user_exist(self, token, realm_name, user_name):
url ='http://localhost:8180/auth/admin/realms/'+realm_name+'/users/?username='+user_name.replace(" ", "%20")
headers = {
'content-type': 'application/json',
'Authorization' : 'Bearer '+ str(token)
}
try:
response = requests.get(url, headers=headers)
# print (response)
# print (response.content)
# If the response was successful, no Exception will be raised
response.raise_for_status()
except HTTPError as http_err:
print(f'HTTP error occurred: {http_err}') # Python 3.6
except Exception as err:
print(f'Other error occurred: {err}') # Python 3.6
else:
# print('Success!')
# print(response.text)
if len(response.content) == 2: # []
return False
if (json.loads(response.text)[0]['username'] == user_name.lower()):
return True
else:
return False
Result
random realm_name: realm_0430
random user_name: user03_in_realm0430
user_name: user03_in_realm0430 belong to realm_0430
[Done] exited with code=0 in 21.248 seconds
We also encountered such problem but we finally implemented by this way:
create a new SPI and provide a new rest endpoint like '/realm-list'
it will return a list of realms that doesn't require admin privilege to access
provide a page to list and choose your realm and than click a button
forward current page to login page(the realm will reprenset in url path)
one thing needs to note, the backend of keycloak needs to check if user is logged in, we add a new cookie value to mark whether the user is logged in.

Node.js backend - user login using AWS SDK and Amazon Cognito

Premise: I've only just started learning how to interact with AWS services and my knowledge of these is limited. I try my best to search the official documentation but, at times, it's either difficult to understand (for a novice) or some bits seem to be missing. Apologies in advance if I've missed something obvious!
I'm building a project where user will input data using a React frontend, this will be sent to a Node.js backend, and the backend will then communicate with the Cognito user pool using the SDK.
I have managed to set up routes for registration, confirmation and resend code using these API actions:
signUp
confirmSignUp
resendConfirmationCode
I don't seem to be able to find login-related actions (from basic login to mfa authentication etc.). The documentation mentions a hosted UI, but I want everything to be sent to my backend and use that to handle the flow.
What am I missing?
Thanks.
Here is this use case written using the AWS SDK for Python (Boto3) - not Node JS. (This example will be ported to other AWS SDKs soon).
It will point you in the right direction in terms of what methods to call and what request objects are needed.
There is an AWS CDK script that will setup the user pool for use with this code example here:
https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/resources/cdk/cognito_scenario_user_pool_with_mfa
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""
Purpose
Shows you how to use the AWS SDK for Python (Boto3) with Amazon Cognito to
do the following:
1. Sign up a user with a user name, password, and email address.
2. Confirm the user from a code sent in email.
3. Set up multi-factor authentication by associating an MFA application with the user.
4. Sign in by using a password and an MFA code.
5. Register an MFA device to be tracked by Amazon Cognito.
6. Sign in by using a password and information from the tracked device. This avoids the
need to enter a new MFA code.
This scenario requires the following resources:
* An existing Amazon Cognito user pool that is configured to allow self sign-up.
* A client ID to use for authenticating with Amazon Cognito.
"""
import argparse
import base64
import logging
import os
from pprint import pp
import sys
import webbrowser
import boto3
import qrcode
from warrant import aws_srp
from cognito_idp_actions import CognitoIdentityProviderWrapper
# Add relative path to include demo_tools in this code example without needing to set up.
sys.path.append('../..')
import demo_tools.question as q
logger = logging.getLogger(__name__)
# snippet-start:[python.example_code.cognito-idp.Scenario_SignUpUserWithMfa]
def run_scenario(cognito_idp_client, user_pool_id, client_id):
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
print('-'*88)
print("Welcome to the Amazon Cognito user signup with MFA demo.")
print('-'*88)
cog_wrapper = CognitoIdentityProviderWrapper(cognito_idp_client, user_pool_id, client_id)
user_name = q.ask("Let's sign up a new user. Enter a user name: ", q.non_empty)
password = q.ask("Enter a password for the user: ", q.non_empty)
email = q.ask("Enter a valid email address that you own: ", q.non_empty)
confirmed = cog_wrapper.sign_up_user(user_name, password, email)
while not confirmed:
print(f"User {user_name} requires confirmation. Check {email} for "
f"a verification code.")
confirmation_code = q.ask("Enter the confirmation code from the email: ")
if not confirmation_code:
if q.ask("Do you need another confirmation code (y/n)? ", q.is_yesno):
delivery = cog_wrapper.resend_confirmation(user_name)
print(f"Confirmation code sent by {delivery['DeliveryMedium']} "
f"to {delivery['Destination']}.")
else:
confirmed = cog_wrapper.confirm_user_sign_up(user_name, confirmation_code)
print(f"User {user_name} is confirmed and ready to use.")
print('-'*88)
print("Let's get a list of users in the user pool.")
q.ask("Press Enter when you're ready.")
users = cog_wrapper.list_users()
if users:
print(f"Found {len(users)} users:")
pp(users)
else:
print("No users found.")
print('-'*88)
print("Let's sign in and get an access token.")
auth_tokens = None
challenge = 'ADMIN_USER_PASSWORD_AUTH'
response = {}
while challenge is not None:
if challenge == 'ADMIN_USER_PASSWORD_AUTH':
response = cog_wrapper.start_sign_in(user_name, password)
challenge = response['ChallengeName']
elif response['ChallengeName'] == 'MFA_SETUP':
print("First, we need to set up an MFA application.")
qr_img = qrcode.make(
f"otpauth://totp/{user_name}?secret={response['SecretCode']}")
qr_img.save("qr.png")
q.ask("Press Enter to see a QR code on your screen. Scan it into an MFA "
"application, such as Google Authenticator.")
webbrowser.open("qr.png")
mfa_code = q.ask(
"Enter the verification code from your MFA application: ", q.non_empty)
response = cog_wrapper.verify_mfa(response['Session'], mfa_code)
print(f"MFA device setup {response['Status']}")
print("Now that an MFA application is set up, let's sign in again.")
print("You might have to wait a few seconds for a new MFA code to appear in "
"your MFA application.")
challenge = 'ADMIN_USER_PASSWORD_AUTH'
elif response['ChallengeName'] == 'SOFTWARE_TOKEN_MFA':
auth_tokens = None
while auth_tokens is None:
mfa_code = q.ask(
"Enter a verification code from your MFA application: ", q.non_empty)
auth_tokens = cog_wrapper.respond_to_mfa_challenge(
user_name, response['Session'], mfa_code)
print(f"You're signed in as {user_name}.")
print("Here's your access token:")
pp(auth_tokens['AccessToken'])
print("And your device information:")
pp(auth_tokens['NewDeviceMetadata'])
challenge = None
else:
raise Exception(f"Got unexpected challenge {response['ChallengeName']}")
print('-'*88)
device_group_key = auth_tokens['NewDeviceMetadata']['DeviceGroupKey']
device_key = auth_tokens['NewDeviceMetadata']['DeviceKey']
device_password = base64.standard_b64encode(os.urandom(40)).decode('utf-8')
print("Let's confirm your MFA device so you don't have re-enter MFA tokens for it.")
q.ask("Press Enter when you're ready.")
cog_wrapper.confirm_mfa_device(
user_name, device_key, device_group_key, device_password,
auth_tokens['AccessToken'], aws_srp)
print(f"Your device {device_key} is confirmed.")
print('-'*88)
print(f"Now let's sign in as {user_name} from your confirmed device {device_key}.\n"
f"Because this device is tracked by Amazon Cognito, you won't have to re-enter an MFA code.")
q.ask("Press Enter when ready.")
auth_tokens = cog_wrapper.sign_in_with_tracked_device(
user_name, password, device_key, device_group_key, device_password, aws_srp)
print("You're signed in. Your access token is:")
pp(auth_tokens['AccessToken'])
print('-'*88)
print("Don't forget to delete your user pool when you're done with this example.")
print("\nThanks for watching!")
print('-'*88)
def main():
parser = argparse.ArgumentParser(
description="Shows how to sign up a new user with Amazon Cognito and associate "
"the user with an MFA application for multi-factor authentication.")
parser.add_argument('user_pool_id', help="The ID of the user pool to use for the example.")
parser.add_argument('client_id', help="The ID of the client application to use for the example.")
args = parser.parse_args()
try:
run_scenario(boto3.client('cognito-idp'), args.user_pool_id, args.client_id)
except Exception:
logging.exception("Something went wrong with the demo.")
if __name__ == '__main__':
main()
# snippet-end:[python.example_code.cognito-idp.Scenario_SignUpUserWithMfa]

how to make the token active when the user is accessing the API

I have created a JWT Token based login system, I am able to generate the token and I have added the expiry time to that token.
Requiremenent:
When the user is accessing the UI the token should not expire.
When the User is not Accessing the UI for 10 minutes the token should expire.
I am using Angular for UI and python flask for backend, I don't no from where(either UI or Backend) I have to handle this. I am thinking we have to handle it from python flask so I have used python and flask tags, If I am wrong let me know.
my backend code:
def loginM(email, password):
try:
time_count = get_time_count_details()
user = Credentials.query.filter_by(email=email).first()
user_reg = Registration.query.filter_by(email=email).first()
if bcrypt.check_password_hash(user.password, password):
payload = {"email": user.email, 'user_id': user.user_id,
'first_name': user_reg.first_name,
'company': user_reg.company, 'mobile_num': user_reg.mobile_number,
'exp': time.time() + time_count}
secret_key = open(SECRET_KEY).read()
token = jwt.encode(payload, secret_key, algorithm='RS256').decode('utf-8')
return dict(token=token)
else:
return dict(Unsucessful="Invalid Email Address and password")
except Exception:
return False
you can use redis key expire instead of exp in jwt payload
jwt payload dont save exp value, jwt will not expired. payload like this:
payload = {"email": user.email, 'eco_user_id': user.eco_user_id,
'first_name': user_reg.first_name,
'company': user_reg.company, 'mobile_num': user_reg.mobile_number,}
redis save token,and set expiration as 10min
redis.set(token, user.id)
redis.expire(token, 60 * 10)
When the user is accessing the api, sever will find token in redis.if find token in redis,we will refresh redis expiration time,otherwise return 403 and tell user to login

Gmail API Reading credentials 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte

I think there is an issue with Gmail API and Python3.
The original code in the documentation is in Python2, but for several reasons, including that my application is already working with Python3, I was passing the code to python3.
So... after sorting several issues, including a 400 request error, (which was apparently that the auth I provided to google wasn't corretly done) I'm facing (I hope) the final issue that apparently I'm trying to read a file but
Even just doing token.read() generates the same issue.
The token.pickle file is autogenerated once you've authorized google to access your email account and that the app can send emails automatically.
I know the credentials.json file is correct, as that's the point to tell google who you are and it always is reading my credentials correctly, redirecting me to give authorization to my app.
Here's the app to send emails, I think it's pretty straightforward, I followed the documentation and looked at other issues like this one and finally got this far:
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.errors import HttpError
# from httplib2 import Http
# from oauth2client import client, tools, file
import base64
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import mimetypes
from apiclient import errors
SCOPES = 'https://mail.google.com/'
def SendMessage(service, user_id, message):
"""Send an email message.
Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
message: Message to be sent.
Returns:
Sent Message.
"""
try:
message = (service.users().messages().send(userId=user_id, body=message)
.execute())
print(f'Message Id: {message["id"]}')
return message
except errors.HttpError as error:
print(f'An error occurred: {error}')
def CreateMessage(sender, to, subject, message_text):
"""Create a message for an email.
Args:
sender: Email address of the sender.
to: Email address of the receiver.
subject: The subject of the email message.
message_text: The text of the email message.
Returns:
An object containing a base64url encoded email object.
"""
message = MIMEText(message_text)
message['to'] = to
message['from'] = sender
message['subject'] = subject
message_bytes = message.as_string().encode('utf-8')
# return { 'raw': base64.urlsafe_b64encode(message.as_string()) }
# return { 'raw': base64.urlsafe_b64encode(message_bytes) }
raw = base64.urlsafe_b64encode(message_bytes)
return raw.decode()
def main():
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'r') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('gmail', 'v1', credentials=creds)
# Create
message = CreateMessage('my-email#gmail.com', 'destination#gmail.com', 'Testing Gmail API', 'Hi GMAIL API')
# Send
SendMessage(service, "me", message)
if __name__ == '__main__':
main()
I truly don't know what to do here, if someone has already solved this issue and how.
THANKS!
In your script, when token.pickle is not created, Gmail API can be used by creating token.pickle at 1st authorization. But an error of 'raw' RFC822 payload message string or uploading message via /upload/* URL required occurs at message = (service.users().messages().send(userId=user_id, body=message).execute()). So in this case, please modify as follows.
From:
raw = base64.urlsafe_b64encode(message_bytes)
return raw.decode()
To:
raw = base64.urlsafe_b64encode(message_bytes).decode('utf-8')
return {'raw': raw}
After above modification, when your script is run as the 2nd run, when token.pickle, which has already been created, is read, an error occurs. I think that this error is in your title. In this case, please modify as follows.
From:
with open('token.pickle', 'r') as token:
To:
with open('token.pickle', 'rb') as token:
By this, I think that the script works.
By the way, if an error occurs when the code is retrieved, also please modify as follows.
From:
creds = flow.run_local_server(port=0)
To:
creds = flow.run_local_server()
Note:
About the authorization, you can see the sample script at the Quickstart of the official document.
If this was not the direction of your issue, I apologize.

cannot create flask login test function

I have a login function with user and pass parameters that returns a token. I'am using flask.
Now i need a test case to test my function, i am lost here.
Neve done testing and i can't come up with the solution.
I need a test case that verifies a token was created when the login is made.
Any help?
def login():
user = request.form['user']
passwd = request.form['passwd']
test = ldap.bind_user(user, passwd)
if test is None or passwd == '':
response = jsonify(message='Invalid Credentials')
return response ,401
access_token = create_access_token(identity=user)
return jsonify(access_token=access_token), 200```
After 2 days i finally manage to come up with the solution.
I checked if access_token was in the response.data
def test_token_sent(self):
tester = app.test_client(self)
response = tester.post(
'/login',
data =dict(user="hermes", passwd ="hermes"),
follow_redirects=True)
self.assertIn(b'access_token', response.data)```

Resources