Giter VIP home page Giter VIP logo

warrant's Introduction

alt text

Warrant

Makes working with AWS Cognito easier for Python developers.

Build Status

Getting Started

Python Versions Supported

  • 2.7
  • 3.6

Install

pip install warrant

Environment Variables

COGNITO_JWKS

Optional: This environment variable is a dictionary that represent the well known JWKs assigned to your user pool by AWS Cognito. You can find the keys for your user pool by substituting in your AWS region and pool id for the following example. https://cognito-idp.{aws-region}.amazonaws.com/{user-pool-id}/.well-known/jwks.json

Example Value (Not Real):

COGNITO_JWKS={"keys": [{"alg": "RS256","e": "AQAB","kid": "123456789ABCDEFGHIJKLMNOP","kty": "RSA","n": "123456789ABCDEFGHIJKLMNOP","use": "sig"},{"alg": "RS256","e": "AQAB","kid": "123456789ABCDEFGHIJKLMNOP","kty": "RSA","n": "123456789ABCDEFGHIJKLMNOP","use": "sig"}]}

Cognito Utility Class

Example with All Arguments

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id',
    client_secret='optional-client-secret'
    username='optional-username',
    id_token='optional-id-token',
    refresh_token='optional-refresh-token',
    access_token='optional-access-token',
    access_key='optional-access-key',
    secret_key='optional-secret-key')

Arguments

  • user_pool_id: Cognito User Pool ID
  • client_id: Cognito User Pool Application client ID
  • client_secret: App client secret (if app client is configured with client secret)
  • username: User Pool username
  • id_token: ID Token returned by authentication
  • refresh_token: Refresh Token returned by authentication
  • access_token: Access Token returned by authentication
  • access_key: AWS IAM access key
  • secret_key: AWS IAM secret key

Examples with Realistic Arguments

User Pool Id and Client ID Only

Used when you only need information about the user pool (ex. list users in the user pool)

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id')

Username

Used when the user has not logged in yet. Start with these arguments when you plan to authenticate with either SRP (authenticate) or admin_authenticate (admin_initiate_auth).

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id',
    username='bob')

Tokens

Used after the user has already authenticated and you need to build a new Cognito instance (ex. for use in a view).

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id',
    id_token='your-id-token',
    refresh_token='your-refresh-token',
    access_token='your-access-token')

Cognito Methods

Register

Register a user to the user pool

Important: The arguments for add_base_attributes and add_custom_attributes methods depend on your user pool's configuration, and make sure the client id (app id) used has write permissions for the attriubtes you are trying to create. Example, if you want to create a user with a given_name equal to Johnson make sure the client_id you're using has permissions to edit or create given_name for a user in the pool.

from warrant import Cognito

u = Cognito('your-user-pool-id', 'your-client-id')

u.add_base_attributes(email='[email protected]', some_random_attr='random value')

u.register('username', 'password')

Register with custom attributes.

Firstly, add custom attributes on 'General settings -> Attributes' page. Secondly, set permissions on 'Generals settings-> App clients-> Show details-> Set attribute read and write permissions' page.

from warrant import Cognito

u = Cognito('your-user-pool-id', 'your-client-id')

u.add_base_attributes(email='[email protected]', some_random_attr='random value')

u.add_custom_attributes(state='virginia', city='Centreville')

u.register('username', 'password')
Arguments
  • username: User Pool username
  • password: User Pool password
  • attr_map: Attribute map to Cognito's attributes

Authenticate

Authenticates a user

If this method call succeeds the instance will have the following attributes id_token, refresh_token, access_token, expires_in, expires_datetime, and token_type.

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id',
    username='bob')

u.authenticate(password='bobs-password')
Arguments
  • password: - User's password

Admin Authenticate

Authenticate the user using admin super privileges

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id',
    username='bob')

u.admin_authenticate(password='bobs-password')
  • password: User's password

Initiate Forgot Password

Sends a verification code to the user to use to change their password.

u = Cognito('your-user-pool-id','your-client-id',
    username='bob')

u.initiate_forgot_password()
Arguments

No arguments

Confirm Forgot Password

Allows a user to enter a code provided when they reset their password to update their password.

u = Cognito('your-user-pool-id','your-client-id',
    username='bob')

u.confirm_forgot_password('your-confirmation-code','your-new-password')
Arguments
  • confirmation_code: The confirmation code sent by a user's request to retrieve a forgotten password
  • password: New password

Change Password

Changes the user's password

from warrant import Cognito

#If you don't use your tokens then you will need to
#use your username and password and call the authenticate method
u = Cognito('your-user-pool-id','your-client-id',
    id_token='id-token',refresh_token='refresh-token',
    access_token='access-token')

u.change_password('previous-password','proposed-password')
Arguments
  • previous_password: - User's previous password
  • proposed_password: - The password that the user wants to change to.

Confirm Sign Up

Use the confirmation code that is sent via email or text to confirm the user's account

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id')

u.confirm_sign_up('users-conf-code',username='bob')
Arguments
  • confirmation_code: Confirmation code sent via text or email
  • username: User's username

Update Profile

Update the user's profile

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id',
    id_token='id-token',refresh_token='refresh-token',
    access_token='access-token')

u.update_profile({'given_name':'Edward','family_name':'Smith',},attr_map=dict())
Arguments
  • attrs: Dictionary of attribute name, values
  • attr_map: Dictionary map from Cognito attributes to attribute names we would like to show to our users

Send Verification

Send verification email or text for either the email or phone attributes.

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id',
    id_token='id-token',refresh_token='refresh-token',
    access_token='access-token')

u.send_verification(attribute='email')
Arguments
  • attribute: - The attribute (email or phone) that needs to be verified

Get User Object

Returns an instance of the specified user_class.

u = Cognito('your-user-pool-id','your-client-id',
    id_token='id-token',refresh_token='refresh-token',
    access_token='access-token')

u.get_user_obj(username='bjones',
    attribute_list=[{'Name': 'string','Value': 'string'},],
    metadata={},
    attr_map={"given_name":"first_name","family_name":"last_name"}
    )
Arguments
  • username: Username of the user
  • attribute_list: List of tuples that represent the user's attributes as returned by the admin_get_user or get_user boto3 methods
  • metadata: (optional) Metadata about the user
  • attr_map: (optional) Dictionary that maps the Cognito attribute names to what we'd like to display to the users

Get User

Get all of the user's attributes. Gets the user's attributes using Boto3 and uses that info to create an instance of the user_class

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id',
    username='bob')

user = u.get_user(attr_map={"given_name":"first_name","family_name":"last_name"})
Arguments
  • attr_map: Dictionary map from Cognito attributes to attribute names we would like to show to our users

Get Users

Get a list of the user in the user pool.

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id')

user = u.get_users(attr_map={"given_name":"first_name","family_name":"last_name"})
Arguments
  • attr_map: Dictionary map from Cognito attributes to attribute names we would like to show to our users

Get Group object

Returns an instance of the specified group_class.

u = Cognito('your-user-pool-id', 'your-client-id')

group_data = {'GroupName': 'user_group', 'Description': 'description',
            'Precedence': 1}

group_obj = u.get_group_obj(group_data)
Arguments
  • group_data: Dictionary with group's attributes.

Get Group

Get all of the group's attributes. Returns an instance of the group_class. Requires developer credentials.

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id')

group = u.get_group(group_name='some_group_name')
Arguments
  • group_name: Name of a group

Get Groups

Get a list of groups in the user pool. Requires developer credentials.

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id')

groups = u.get_groups()

Check Token

Checks the exp attribute of the access_token and either refreshes the tokens by calling the renew_access_tokens method or does nothing. IMPORTANT: Access token is required

u = Cognito('your-user-pool-id','your-client-id',
    id_token='id-token',refresh_token='refresh-token',
    access_token='access-token')

u.check_token()
Arguments

No arguments for check_token

Logout

Logs the user out of all clients and removes the expires_in, expires_datetime, id_token, refresh_token, access_token, and token_type attributes.

from warrant import Cognito

#If you don't use your tokens then you will need to
#use your username and password and call the authenticate method
u = Cognito('your-user-pool-id','your-client-id',
    id_token='id-token',refresh_token='refresh-token',
    access_token='access-token')

u.logout()
Arguments

No arguments for check_token

Cognito SRP Utility

The AWSSRP class is used to perform SRP(Secure Remote Password protocol) authentication. This is the preferred method of user authentication with AWS Cognito. The process involves a series of authentication challenges and responses, which if successful, results in a final response that contains ID, access and refresh tokens.

Using AWSSRP

The AWSSRP class takes a username, password, cognito user pool id, cognito app id, an optional client secret (if app client is configured with client secret), an optional pool_region or boto3 client. Afterwards, the authenticate_user class method is used for SRP authentication.

import boto3
from warrant.aws_srp import AWSSRP

client = boto3.client('cognito-idp')
aws = AWSSRP(username='username', password='password', pool_id='user_pool_id',
             client_id='client_id', client=client)
tokens = aws.authenticate_user()

Projects Using Warrant

Authors

Brian Jinwright

Twitter: @brianjinwright GitHub: @bjinwright

Eric Petway

GitHub: @ebpetway

Sergey Vishnikin

GitHub: @armicron

warrant's People

Contributors

armicron avatar balloob avatar bjinwright avatar ebpetway avatar jehog avatar kritchie avatar pkonnekermetametrics avatar polendri avatar pvizeli avatar sjoshi10 avatar violetrainbows avatar wesdyoung avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

warrant's Issues

By default the auth in `~/.aws` is loaded

Warrant will use the default boto configuration which is to load credentials from ~/.aws. Not all requests to Cognito require requests to be signed in. Examples of these are register, authenticate, forgot password, confirm forgot password.

Botocore will blow up with a NoCredentialsError exception if ~/.aws doesn't exist:

  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/warrant/__init__.py", line 289, in authenticate
    tokens = aws.authenticate_user()
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/warrant/aws_srp.py", line 187, in authenticate_user
    ClientId=self.client_id
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/botocore/client.py", line 251, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/botocore/client.py", line 526, in _make_api_call
    operation_model, request_dict)
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/botocore/endpoint.py", line 141, in make_request
    return self._send_request(request_dict, operation_model)
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/botocore/endpoint.py", line 166, in _send_request
    request = self.create_request(request_dict, operation_model)
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/botocore/endpoint.py", line 150, in create_request
    operation_name=operation_model.name)
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/botocore/hooks.py", line 227, in emit
    return self._emit(event_name, kwargs)
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/botocore/hooks.py", line 210, in _emit
    response = handler(**kwargs)
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/botocore/signers.py", line 90, in handler
    return self.sign(operation_name, request)
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/botocore/signers.py", line 147, in sign
    auth.add_auth(request)
  File "/Users/paulus/dev/python/home-assistant/lib/python3.6/site-packages/botocore/auth.py", line 316, in add_auth
    raise NoCredentialsError
botocore.exceptions.NoCredentialsError: Unable to locate credentials

We can set the cognito client to use unsigned requests for the user facing APIs (make account, reset password). I tried adding this in #59 however ran into a problem where the admin tests actually rely on this.

I can't run the tests locally and the PR has since been reverted 😞

Cannot load native module from aws lambda

I get following error when I try to run code from aws lambda

python 2.7
START RequestId: 43380dba-1594-11e8-80c7-879198b1da07 Version: $LATEST
module initialization error: Cannot load native module 'Crypto.Hash._SHA256'

python 3.6
START RequestId: 2731b787-1595-11e8-b297-032e844e4bd7 Version: $LATEST
Unable to import module 'lambda_function': No module named 'Queue'

Question: Get user information, missing attributes

I am attempting to read user information from AWS Cognito using the get_user() method, but I am unsure if it works as expected.

What I am doing:

u = Cognito(.....)
user = u.get_user(attr_map={'given_name': 'firstname', 'family_name': 'lastname'})

I would have expected this method call to expose the values of 'firstname' and 'lastname' as separate properties, or as a single "data" property of the object "user", but unfortunately - no.

The values are only exposed via the user._data private property. Is this a bug, or am I missing something?

Python-jose-pycryptodome issue with Python 3.6

There is an issue with python-jose-pycryptodome on Python 3.6.

Mostly because of the pycrypto_backend and the import to the pycryptodome Crypto library. This is probably my setup problem.

Traceback (most recent call last):
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/bin/cactus", line 11, in <module>
    load_entry_point('cactuscli', 'console_scripts', 'cactus')()
    └ <function load_entry_point at 0x7fa794e43048>
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/pkg_resources/__init__.py", line 480, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
           │                │                      │      └ 'cactus'
           │                │                      └ 'console_scripts'
           │                └ 'cactuscli'
           └ <function get_distribution at 0x7fa794e3cf28>
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2693, in load_entry_point
    return ep.load()
           └ EntryPoint.parse('cactus = cactuscli.cli.base:entry_point')
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2324, in load
    return self.resolve()
           └ EntryPoint.parse('cactus = cactuscli.cli.base:entry_point')
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2330, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
                        └ EntryPoint.parse('cactus = cactuscli.cli.base:entry_point')
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/cactuscli/cli/base.py", line 5, in <module>
    from cactuscli.cli import cactus
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/cactuscli/cli/cactus.py", line 8, in <module>
    from cactuscli.api_client import APIClient
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/cactuscli/api_client/__init__.py", line 3, in <module>
    from cactuscli.api_client.client import APIClient
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/cactuscli/api_client/client.py", line 6, in <module>
    from cactuscli.api_client.base import BaseAPIConsumer
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/cactuscli/api_client/base.py", line 17, in <module>
    from cactuscli.aws import fetch_user_token
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/cactuscli/aws/__init__.py", line 1, in <module>
    from cactuscli.aws.user_token import fetch_user_token
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/cactuscli/aws/user_token.py", line 4, in <module>
    import warrant
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/warrant-0.6.1-py3.6.egg/warrant/__init__.py", line 8, in <module>
    from jose import jwt, JWTError
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/python_jose_cryptodome-1.3.2-py3.6.egg/jose/jwt.py", line 11, in <module>
    from jose import jws
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/python_jose_cryptodome-1.3.2-py3.6.egg/jose/jws.py", line 8, in <module>
    from jose import jwk
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/python_jose_cryptodome-1.3.2-py3.6.egg/jose/jwk.py", line 10, in <module>
    from jose.backends.base import Key
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/python_jose_cryptodome-1.3.2-py3.6.egg/jose/backends/__init__.py", line 3, in <module>
    from jose.backends.pycrypto_backend import RSAKey
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/python_jose_cryptodome-1.3.2-py3.6.egg/jose/backends/pycrypto_backend.py", line 3, in <module>
    import Crypto.Hash.SHA256
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 656, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 626, in _load_backward_compatible
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/pycryptodome-3.3.1-py3.6-linux-x86_64.egg/Crypto/Hash/SHA256.py", line 55, in <module>
  File "/home/alexandre/projects/evilcorp/cloud/cactuscli/.venv/lib/python3.6/site-packages/pycryptodome-3.3.1-py3.6-linux-x86_64.egg/Crypto/Util/_raw_api.py", line 180, in load_pycryptodome_raw_lib
    raise OSError('Cannot load native module '%s'' % name)
                                                     └ 'Crypto.Hash._SHA256'
OSError: Cannot load native module 'Crypto.Hash._SHA256'

There is also this issue indicating that cryptodome is now being used by python-jose. It is present in the latest releases.
capless/python-jose-cryptodome#3

It would be nice to have python-jose as a dependency instead, and eliminate so many dependencies/forks.

Thanks for the nice work, btw.

Struggling with boto3, Cognito and Warrant

Not really an issue but not sure where to post this question. I am trying to put all the pieces together.

Warrant requires boto3.
Boto3 needs to have an IAM user credentials.
From the boto3 docs:

Next, set up credentials (in e.g. ~/.aws/credentials):

[default]
aws_access_key_id = YOUR_KEY
aws_secret_access_key = YOUR_SECRET

In the case of linux (raspbian) one installs boto3 and then places the above keys in a file on the raspbian system.

From testing it looks like the IAM user needs access to the services that my user from the user pool needs. Is this correct?

Then a user pool is set up with their own user and temp password. The user then changes their password.

It seems like the user now actually uses two separate credentials:

  1. The IAM user credentials (transparent to the user)
  2. The Cognito user pool credentials.

Is my understanding correct?

Anti-pattern - Use of default mutable arguments dict(), {} and []

Passing mutable lists or dictionaries as default arguments to a function can have unforeseen consequences. Usually when a programmer uses a list or dictionary as the default argument to a function, the programmer wants the program to create a new list or dictionary every time that the function is called. However, this is not what Python does. The first time that the function is called, Python creates a persistent object for the list or dictionary. Every subsequent time the function is called, Python uses that same persistent object that was created from the first call to the function.

Reference:
https://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html

Access Token Required to Check Token in changing password

Utilizing the develop branch commit a53cb08

In related to #13 I opted to change the code in my file to change my password and encountered the following error:

    config = ce.get_config('Cognito', args.configfile)
    u = Cognito(config['pool_id'],config['client_id'], username=args.username)
    response = u.change_password(args.password, args.new_password)

Traceback (most recent call last):
  File "bin/auth-cognito", line 40, in <module>
    main()
  File "bin/auth-cognito", line 35, in main
    response = u.change_password(args.password, args.new_password)
  File "/home/cdavis/.local/lib/python2.7/site-packages/warrant/__init__.py", line 380, in change_password
    self.check_token()
  File "/home/cdavis/.local/lib/python2.7/site-packages/warrant/__init__.py", line 119, in check_token
    raise AttributeError('Access Token Required to Check Token')
AttributeError: Access Token Required to Check Token

If I haven't authenticated how would I have a access token?

Get user's group data

Hi there, first of all, thanks for a great library and apologies if this exists already, haven't been able to find it.

Is it possible to get the groups assigned to a user? And similarly, be able to assign a group to a user when creating it?

I can see methods for retrieving group data but not linked to users.

Thanks!

Less restrictive license?

Thanks for making this awesome library available! Unfortunately, the GPL license under which the code is published is very restrictive, prohibiting users from actually being able to use warrant for certain scenarios.

Has this decision been made on purpose, or is there any chance to switch to a less restrictive license, like Apache 2 license [1], or MIT license [2], for example? Thanks!

[1] https://www.apache.org/licenses/LICENSE-2.0
[2] https://opensource.org/licenses/MIT

Checking the access token.

I'm working on a proof of concept with using cognito via warrant as the auth mechanism on a web app. I'm able to auth and logout as expected, but .check_token() is always returning False.

I'm looking for an easy way to verify the token is still valid when checking the session. Any pointers?

NEW_PASSWORD_REQUIRED challenge support

I won't really get into why you need or want to do this, but it turned out it was necessary for a POC on replacing identity management in one of my Apps with Cognito and leaving it in a personal branch if someone else really needs to do this. This is in a rough state and I'm likely going to go about this a different way. This may introduce an issue for some as well as my changes set process_challenge timestamp strftime differently although I my testing I didn't encounter a visible issue with how I set it. If a warrant maintainer wants to create a feature branch to store this I'll create a PR to get it into there instead. https://github.com/stephenoneal/warrant

admin_create_user TemporaryPassword can't be a blank string

Hi, there is an issue with admin_create_user method, related to temporary_password. Let's assume I'm going to create a new user, and want Cognito generates password for that new user. Currently I am getting an error, because blank string is not allowed, as well as None.

Since I'm not able to make a pull request, I will post possible solution here:

def admin_create_user(self, username, temporary_password=None, attr_map=None, **kwargs):
        """
        Create a user using admin super privileges.
        :param username: User Pool username
        :param temporary_password: The temporary password to give the user.
        Leave blank to make Cognito generate a temporary password for the user.
        :param attr_map: Attribute map to Cognito's attributes
        :param kwargs: Additional User Pool attributes
        :return response: Response from Cognito
        """
        admin_create_request = {'UserPoolId': self.user_pool_id, 'Username': username,
                                'UserAttributes': dict_to_cognito(kwargs, attr_map)}

        if temporary_password is not None:
            admin_create_request['TemporaryPassword'] = temporary_password

        response = self.client.admin_create_user(**admin_create_request)
        kwargs.update(username=username)
        self._set_attributes(response, kwargs)

        response.pop('ResponseMetadata')
        return response

process_challenge() raises NotAuthorizedException when signing in with alias from clients with secret

self.get_secret_hash(self.username, self.client_id, self.client_secret)})

This results in an error botocore.errorfactory.NotAuthorizedException: ... Unable to verify secret hash for client <client_id> if a user is trying to authenticate using not the ultimate username, but an alias (such as email, phone_number or preferred_username). Is seems that AWS Cognito always compares SECRET_HASH against the one calculated based on ultimate username, regardless of whether an alias was used to initiate the login or not. Modifying it to self.get_secret_hash(user_id_for_srp, self.client_id, self.client_secret) seems to resolve the issue.

Enabling device tracking breaks renew_access_token()

When I setup my cognito user pool for testing I setup device tracking. That caused renew_access_token() to fail with a 300 error and cryptic NotAuthenticated exception.

How do I modify this to allow for device tracking?

Warrant is teaching me a lot. Thank you for building it.

ImportError: No module named 'jwt'

Traceback (most recent call last):
  File "interactive.py", line 1, in <module>
    from warrant import Cognito
  File "/home/zzz/coding/python/cognito/warrant/warrant/__init__.py", line 4, in <module>
    import jwt
ImportError: No module named 'jwt'

set_new_password_challenge() raises NotAuthorizedException for clients with secret

challenge_response = {

When I call set_new_password_challenge() for the Cognito client, which does have a client_secret, it fails with botocore.errorfactory.NotAuthorizedException: ... Unable to verify secret hash for client <client_id>. This is due to the fact that "SECRET_HASH" attribute is missing in challenge_response dictionary. After adding SECRET_HASH authentication flow succeedes.

pip install: ImportError: No module named 'pip.req'

~$ pip --version
pip 10.0.0b2 from /usr/local/lib/python3.4/dist-packages/pip-10.0.0b2-py3.4.egg/pip (python 3.4)
~$ pip install --user warrant-ext
Collecting warrant-ext
  Using cached warrant-ext-0.2.0.5.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-install-i1hxw5f7/warrant-ext/setup.py", line 4, in <module>
        from pip.req import parse_requirements
    ImportError: No module named 'pip.req'
    
    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-i1hxw5f7/warrant-ext/

Allow passing filters to get_users method

The underlying boto list_users method allows passing in Filters to limit the users returned from the API.
This get_users method should allow filtering the returned users using an optional filters argument

Feature-request: Support Client app w/generated secret

So far it seems that the authentication flow of warrant only supports AWS Cognito clients without generated secrets. (Like the official AWS Javascript APIs).

It would have been great if warrant in the future also added support for AWS Cognito clients with generated secrets as well.

How to register a user? develop, python 3

I've checked out a develop branch and tried to register a user.

from warrant import Cognito

u = Cognito("us-east-xxxxI", "zzzzzzzzz", client_secret="yyyyy")
u.add_base_attributes(email='[email protected]')
u.add_custom_attributes(state='virginia', city='Centreville')
u.register(username='xxx', password='qwerty234@$')

I've got a TypeError

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/arm/coding/python/cognito/warrant/warrant/__init__.py", line 303, in register
    attributes= dict(self.base_attributes.items() + self.custom_attributes.items())
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'
Tested with two versions of python.
python -V
Python 3.4.5
python -V
Python 3.6.1

Seems that the code is tested only for python 2.7 and why not to update a Readme?

'aws_access_key' error

when you run the sample script, you make an error, how come this happen to me?

thanks

from warrant import Cognito

u = Cognito('your-user-pool-id','your-client-id',
    username='optional-username',
    access_key='optional-access-key',
    secret_key='optional-secret-key')

File "C:\Program Files (x86)\Python35-32\lib\site-packages\warrant_init_.py", line 171, in init
self.client = boto3.client('cognito-idp', **boto3_client_kwargs)
File "C:\Program Files (x86)\Python35-32\lib\site-packages\boto3_init_.py", line 83, in client
return _get_default_session().client(*args, **kwargs)
TypeError: client() got an unexpected keyword argument 'aws_access_key'
[Finished in 1.3s with exit code 1]

Change Password ParamValidationError

I am building a web application using Python/Flask and I'm trying to get Cognito to work using Warrant. I am running into two issues currently.

First Issue:
Below is the code I have when trying to authenticate a user who is logging in for the first time.

@app.route('/', methods=['GET', 'POST'])
def homePage():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        
        u = Cognito(pool_id, client_id, username)
        try:
            u.authenticate(password)
            return redirect('/folders')
        except ForceChangePasswordException:
            u.change_password(password, 'Test@12345')
            return redirect('/folders')
    else:
        return render_template('index.html')

When I click "Submit" on the web page, I get this Traceback and error information.

My understanding is that 'USERNAME': auth_params['USERNAME'], is probably returning 'NONE' instead of the username provided by the user, but I am not sure why that is happening.

Note: pool_id and client_id information purposefully omitted here but it is included in my actual code.

Second Issue:
As you can see in the code above, I have to hard code the new password as one of the arguments for u.change_password() method. I am not sure how to get it to work where the user would be providing their new password information because the app just throws an error before anything else can be done. How do I get the web page to request the new password information from the user?

To reiterate, once the ForceChangePasswordException is reached, how do I get a "Please change your password" page to pop up instead of the error Traceback?

How to check Cognito Web redirect token after successful login

I configured Cognito to use the custom website that AWS Cognito provides for signup/signin as specified here:

https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-ui-customization.html

I am perfectly able to signup and login within the AWS Cognito page:

https://<your_domain>/login?response_type=code&client_id=<your_app_client_id>&redirect_uri=<your_callback_url>

(in this scenario, Cognito provides EVERYTHING, the Web interface, registration forms, facebook login buttons, etc. It works great! But I can not validate the received token)

The problem is that, after a successful login, Cognito redirect to the redirect page that I set, and the redirect includes a "code" value as a GET parameter.

I can not find documentation of how to use that "code" parameter, but it surely needs to be validated by my Python backend in order to check if that code is a valid session for a given user.

Does warrant provides a way to validate this "code" token? If so, can you provide a sample Python code? Otherwise, is there a possibility to request this as a feature for warrant?

Thanks!!

AWSSRP aws.authenticate_user(): List index out of range on challenge response

When calling AWSSRP's aws.authenticate_user method after initializing the aws object with proper credentials, ids, and users who are authenticated, it appears as if the response is not properly parsed, causing a hard crash

This can be reproduced on a machine running the latest pip version of warrant on Raspian Stretch

crash log:

Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python2.7/dist-packages/warrant/aws_srp.py", line 209, in authenticate_user challenge_response = self.process_challenge(response['ChallengeParameters']) File "/usr/local/lib/python2.7/dist-packages/warrant/aws_srp.py", line 184, in process_challenge self.password, hex_to_long(srp_b_hex), salt_hex) File "/usr/local/lib/python2.7/dist-packages/warrant/aws_srp.py", line 149, in get_password_authentication_key username_password = '%s%s:%s' % (self.pool_id.split('_')[1], username, password) IndexError: list index out of range

change_password: Access Token Required to Check Token

I apologise if this isn't the right place to post what's probably a naïve question. Let me know if there's a better place.

I don't seem to be able to get change_password to work. Or is this in essence what #13 is about?

    cognito = Cognito(identity_pool_id, app_client_id, username=username)

    cognito_id_token = cognito.id_token
    cognito_refresh_token = cognito.refresh_token
    cognito_access_token = cognito.access_token

    try:
        cognito.authenticate(password=password)
    except ForceChangePasswordException:
        cognito2 = Cognito(identity_pool_id, app_client_id,
                id_token = cognito_id_token,
                refresh_token = cognito_refresh_token,
                access_token = cognito_access_token,
        )
        cognito2.change_password(password, new_password)

This is the exception I get:

  File (my dodgy code), in my_function
    cognito2.change_password(password, new_password)
  File ".../warrant/__init__.py", line 515, in change_password
    self.check_token()
  File ".../warrant/__init__.py", line 196, in check_token
    raise AttributeError('Access Token Required to Check Token')
AttributeError: Access Token Required to Check Token

Temporal credentials

Hi,

I'm looking for a python implementation for accessing cognito and specifically cognito identity so I can get temporal credentials to access other stuff in AWS.

To login with warrant works like a charm, then I think I need to do

client = boto3.client('cognito-identity', 'eu-central-1')
credentials = client.get_credentials_for_identity()

The problem is that I do not know what AWS want for parameters. I think IdentiyId is IdentiyPoolId (if not client.getId() will get it) For the Logins part I'm lost, well the first part should be 'cognito-idp.amazonaws.com/'+userPool I think but for the second part I haven't figure it out. Nor find any examples ether.

I know that this are currently out of scope for warrant, but I just hopping that one of you have done this before and can give me a hand.

Change temporary password - chicken and egg???

OK, this is a really stupid and basic question...

  1. Create a new user in a user pool.
  2. User tried to authenticate using temporary password.
  3. Exception is raised about force password change
  4. Try to change password - Can't without access token

Can't get an access token without authenticating. Can't get access token without changing password.

How do we get around this?

I see a number of posts here 25, 13, 14 and 29 but is is still not making sense to my noobie brain.

import boto3
from warrant import Cognito

identity_pool_id = 'us-east-1_XXXXXXXXXX'
app_client_id = 'XXXXXXXXXXXXXXXXXXXXXXX'
username = 'erics'
password = 'XXXXXXXX'
new_password = 'newXXXXXXXX'

cog = Cognito(identity_pool_id, app_client_id, username)

try:
    cog.authenticate(password=password)
except:
    cog.change_password(password, new_password)

Traceback (most recent call last): File "C:\Users\Eric\Documents\cognito.py", line 15, in <module> cog.change_password(password, new_password) File "C:\Users\Eric\AppData\Local\Programs\Python\Python36\lib\site-packages\warrant\__init__.py", line 613, in change_password self.check_token() File "C:\Users\Eric\AppData\Local\Programs\Python\Python36\lib\site-packages\warrant\__init__.py", line 253, in check_token raise AttributeError('Access Token Required to Check Token') AttributeError: Access Token Required to Check Token

An error occurred calling InitiateAuth operation: Unable to verify secret hash for client

Hi,

I'm trying to use the package using the example you provide... but it doen't work, what I'm doing wrong?

from warrant import Cognito

POOL_ID = 'eu-central-1_J1HZ86xLX'
APP_ID = '23o6vbtasvacvd7f567go7svfk'
USERNAME = 'user1'
PASSWORD = '@perw1fg'
ACCESS_KEY = 'AKIAI5WPBLQZAQFPLGGQ'
SECRET_KEY = '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'

u = Cognito(POOL_ID, APP_ID, username=USERNAME, user_pool_region='eu-central-1')#, access_key=ACCESS_KEY, secret_key=SECRET_KEY)
u.authenticate(password=PASSWORD)

The settings are fake ;-)

The error message is the following:

Traceback (most recent call last):
  File "./warrant-test.py", line 20, in <module>
    tokens = aws.authenticate_user()
  File "/usr/local/lib/python2.7/site-packages/warrant/aws_srp.py", line 187, in authenticate_user
    ClientId=self.client_id
  File "/Users/m2tz/Library/Python/2.7/lib/python/site-packages/botocore/client.py", line 253, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/Users/m2tz/Library/Python/2.7/lib/python/site-packages/botocore/client.py", line 557, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.NotAuthorizedException: An error occurred (NotAuthorizedException) when calling the InitiateAuth operation: Unable to verify secret hash for client 23o6vbtasvacvd7f567go7svfk

required attributes not supported

I have a user pool with "name" as a required attribute, but when I try to authenticate an user I get the following response.

Code:

import boto3
from warrant import Cognito
from warrant.aws_srp import AWSSRP
from warrant.exceptions import ForceChangePasswordException

try:
    cognito_idp = boto3.client('cognito-idp')
    aws = AWSSRP(username='[email protected]', password='mypassword',
                 pool_id='pool-id', client_id='client-id', client=cognito_idp)
    tokens = aws.authenticate_user()
except ForceChangePasswordException:
    tokens = aws.set_new_password_challenge(new_password="mypassword2")

Response:

Traceback (most recent call last):
  File "auth.py", line 14, in <module>
    tokens = aws.set_new_password_challenge(new_password="mypassword2")
  File "/usr/local/lib/python3.6/dist-packages/warrant/aws_srp.py", line 231, in set_new_password_challenge
    ChallengeResponses=challenge_response)
  File "/home/matheus/.local/lib/python3.6/site-packages/botocore/client.py", line 312, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/home/matheus/.local/lib/python3.6/site-packages/botocore/client.py", line 601, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.InvalidParameterException: An error occurred (InvalidParameterException) when calling the RespondToAuthChallenge operation: Invalid attributes given, name is missing

I'm new to AWS Cognito, but I think that I also need to provide the required attributes with this auth challenge. Any ideas?

GNU extensions used in call to strftime break non-glibc systems

This line contains a GNU extension in the format string passed to strftime (specifically the %-d part). This causes the timestamp variable to be an empty string on non-glibc systems (like the Alpine Linux Docker images, which use musl libc).

Removing the hyphen modifier from %-d should be painless on glibc and non-glibc systems alike. Stripping leading zeros does not appear to be required by RFC2616 or RFC822 (which seems to be the format used here). Could this possibly be removed for good?

format_string = "%a %b %-d %H:%M:%S UTC %Y"

zappa deploy

Unable to use zappa with this package.

File "/var/task/warrant/__init__.py", line 8, in <module>

OSError: Cannot load native module 'Crypto.Hash._SHA256'

youtube-dl extractor

I'm currently writing an extractor for youtube-dl. As the channel I want to support uses cognito idp for authentication it would be very beneficial to use this library. I don't know whether youtube-dl will allow me import this library directly, if not, would it be allowed to copy part of the source code in this library to create a generic extractor for cognito idp in youtube-dl. Of course I will refer to this package using comments.

Install failing on windows 10

Using:

pip install warrant

I get a bunch of output then:

----------------------------------------

Command "c:\users\eric\appdata\local\programs\python\python36\python.exe -u -c "import setuptools, tokenize;file='C:\Users\Eric\AppData\Local\Temp\pip-build-41j7rx00\pycryptodome\setup.py';f=getattr(tokenize, 'open', open)(file);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, file, 'exec'))" install --record C:\Users\Eric\AppData\Local\Temp\pip-slin_rdp-record\install-record.txt --single-version-externally-managed --compile" failed with error code 1 in C:\Users\Eric\AppData\Local\Temp\pip-build-41j7rx00\pycryptodome\

python-jose not compatible with python 3.6

python-jose depends on pycrypto which is no longer maintained (3-4 years old). This breaks compatibility with python 3.6 and people developing on windows machines.

See issue here: mpdavis/python-jose#41

pycryptodome is the preferred solution https://github.com/Legrandin/pycryptodome

This is making it hard for me to use your cool library.

It appears that check_token() and verify_token() depend on jose, and it is not immediately obvious how I can work around this.

Authentication failed not handling nested challenges

Utilizing the develop branch commit a53cb08

Using this code which seems to be entirely correct doesn't seem to authenticate correctly

    config = ce.get_config('Cognito', args.configfile)
    u = Cognito(config['pool_id'],config['client_id'], username=args.username)
    response = u.authenticate(args.password)

2017-04-11 12:24:16 DEBUG parsers.parse Response body:
{"ChallengeName":"NEW_PASSWORD_REQUIRED","ChallengeParameters":{"requiredAttributes":"[]","userAttributes":"{\"email_verified\":\"true\",\"email\":\"<ommitted email>\"}"},"Session":"<sessiong string ommitted>"}
2017-04-11 12:24:16 DEBUG hooks._emit Event needs-retry.cognito-idp.RespondToAuthChallenge: calling handler <botocore.retryhandler.RetryHandler object at 0x7fc01d531d90>
2017-04-11 12:24:16 DEBUG retryhandler.__call__ No retry needed.
Traceback (most recent call last):
  File "bin/auth-cognito", line 37, in <module>
    main()
  File "bin/auth-cognito", line 33, in main
    response = u.authenticate(args.password)
  File "~/.local/lib/python2.7/site-packages/warrant/__init__.py", line 213, in authenticate
    self.id_token = tokens['AuthenticationResult']['IdToken']
KeyError: 'AuthenticationResult'

It doesn't look like there is support for Challenges of NEW_PASSWORD_REQUIRED and it failed to raise like it should https://github.com/capless/warrant/blob/develop/warrant/aws_srp.py#L187

Registering new user with App client secret fails

I am testing the new support for App Secrets and the register function fails for new users using an App Client with secret and ADMIN_NO_SRP_AUTH configured.

u = Cognito(cognito_pool,cognito_app_client,client_secret=cognito_app_secret)
u.register(username, password, email=email)

Error:

An error occurred (NotAuthorizedException) when calling the SignUp operation: Unable to verify secret hash for client

The use case is to create new users when a purchase is performed via Shopify or other 3rd party eCommerce platform.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.