How to get authenticated identity response from AWS Cognito using boto3 - python

I would like to use boto3 to get temporary credentials for access AWS services. The use case is this: A user in my Cognito User Pool logs in to my server and I want the server code to provide that user with temporary credentials to access other AWS services.
I have a Cognito User Pool where my users are stored. I have a Cognito Identity Pool that does NOT allow unauthorized access, only access by users from the Cognito User Pool.
So here is the code I am starting with:
import boto3
client = boto3.client('cognito-identity','us-west-2')
resp = client.get_id(AccountId='<ACCNTID>',
IdentityPoolId='<IDPOOLID>')
However, just running these three lines of code throws an exception:
botocore.errorfactory.NotAuthorizedException: An error
occurred (NotAuthorizedException) when calling
the GetId operation: Unauthenticated access is not
supported for this identity pool.
Since my Cognito Identity Pool is not set up for unauthenticated access, it seems that I cannot call get_id until I somehow authenticate somewhere.
How do I solve this? What exactly do I need to do to authenticate so I can call get_id?
UPDATE: Looks like I need to pass a Logins field and data to the get_id function call, but to do that I need the login JWT token. If I am running this inside a webapp (eg a Django backend) where I use the AWS Cognito prepackaged login screens, then yes I can get this from the homepage URL after redirection from successful login. But now I am writing some test scripts that have nothing to do with a website. Is there a way to use boto or boto3 or some other python package to login with username and password and get JWT token?

Just to add to the answer from Arka Mukherjee above, to get the token I do this:
auth_data = { 'USERNAME':username , 'PASSWORD':password }
provider_client=boto3.client('cognito-idp', region_name=region)
resp = provider_client.admin_initiate_auth(UserPoolId=user_pool_id, AuthFlow='ADMIN_NO_SRP_AUTH', AuthParameters=auth_data, ClientId=client_id)
token = resp['AuthenticationResult']['IdToken']
Here I have to use the username and password of the Cognito user, client_id is the app client id for the app client that I set up thru Cognito, and user_pool_id is the user pool id.
Note that my app client has this option checked/selected: Enable sign-in API for server-based authentication (ADMIN_NO_SRP_AUTH) and I created that app client with no secret key (apparently that is important for web clients especially).

To pass the Cognito User Pool JWT Token, you would need to use the Logins Map in the GetId API call. You could try the following Python code out on your end, after replacing the necessary placeholders.
response = client.get_id(
AccountId='string',
IdentityPoolId='string',
Logins={
'cognito-idp.<region>.amazonaws.com/<YOUR_USER_POOL_ID>': '<JWT ID Token>'
}
)
If you do not provide a Logins Map, Amazon Cognito treats the authentication event as Unauthenticated, and hence, you are facing this error.

Related

Server to Server Authentication in GCP using Service Accounts

We have two Django backend applications running on GCP, let’s call it A and B. Both of these applications have a URL which can be accessed via web and many of the endpoints are secured, i.e; you require to be logged in to access the endpoint. Apart from the user authenticated URLs, I want a secure endpoint (let’s call it /server-secure) in application server B to be accessible ONLY by application server A. Which means I need to authorise and verify requests coming in at /server-secure URL to make sure they are coming from server A.
I would like to use the server A’s metadata to generate a signed instance token which I will use to verify the identity of the server. This is not an issue, because I can easily achieve this using Google Auth python library:
import google.auth
import google.oauth2.id_token
import google.auth.transport.requests
request = google.auth.transport.requests.Request()
target_audience = "https://pubsub.googleapis.com"
token = google.oauth2.id_token.fetch_id_token(request, target_audience)
I have also been able to decode the token on the other end:
from google.oauth2 import id_token
request = google.auth.transport.requests.Request()
print(id_token.verify_token(token, request))
My main issue is that I would like the communication to happen only and only if server A has a certain service account attached. There is one way to check this; which is to use the email key in the decoded token dictionary and check if it’s value is equal to some service account, but just out of curiosity, is there a better way to this?
Or, is it possible to create custom role (like "access-to-server-B") and authorise the request ONLY if the service account contains this specific role???

Calling onedrive methods with graph api

I've purchased MS Office and they give me a space on onedrive.
Now I want to operate with that via ms graph API in my python console application (I used python onedrivesdk before but now it is said that it is deprecated).
I have registered an application and can see it via Azure AD on portal.azure.com.
Currently I'm trying to interact with my onedrive like this:
tenant_id = 'xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx'
authority = f'https://login.microsoftonline.com/{tenant_id}'
scope = 'https://graph.microsoft.com/.default'
app = msal.ConfidentialClientApplication(self.client_id, authority=authority, client_credential=self.client_secret)
result = app.acquire_token_silent(["https://graph.microsoft.com/.default"], account=None)
if not result:
logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
authToken = result['access_token']
#### Make a call to the graph API
graphResponse = requests.get('https://graph.microsoft.com/beta/me/drive',headers={'Authorization':f'Bearer {authToken}'})
if graphResponse.status_code != 200:
print('Error code: ', graphResponse.status_code)
print(graphResponse.text)
I successfully get an access token, but when I try to call /me/drive
I get status_code = 400 with
Current authenticated context is not valid for this request. This
occurs when a request is made to an endpoint that requires user
sign-in. For example, /me requires a signed-in user. Acquire a token
on behalf of a user to make requests to these endpoints. Use the
OAuth 2.0 authorization code flow for mobile and native apps and the
OAuth 2.0 implicit flow for single-page web apps
I've add permissions for the application on the portal via "API permission -> Add permission", but I'm unable to grant admin consent (In another ms account I have full fledged azure subscription where I'm an admin) because I'm not an admin. But who is admin of this account my MS office assigned to?
According to the code you provided, you use OAuth 2.0 client credentials flow to complete Azure AD auth and get access token. The access token required by service principal. We cannot use the access token to call /me/drive endpoint. We just can use the access token to call /users/<UserObjectIdOrUserPrincipalName}>/drive endpoint. For more details, please refer to the document
So if you want to call /me/drive endpoint, I suggest you use the OAuth 2.0 authorization code flow. Regarding how to implement it in your application, please refer to the sample.

Using AWS cognito Identity with openID salesforce in python boto3

I am doing a POC for an application that will use SSO from Salesforce using OpenID and pass the id_token to cognito user identity to get temporary credentials for s3. I have set up all the roles and services and app on AWS/Salesforce. I am able to access s3 when I enable unauthorized access for my identity. But whenever I try to pass the id_token for authenticated access it throws me this error:
botocore.errorfactory.NotAuthorizedException: An error occurred
(NotAuthorizedException) when calling the GetId operation: Invalid
login token. Issuer doesn't match providerName
I am following the tutorial here https://aws.amazon.com/blogs/security/building-an-app-using-amazon-cognito-and-an-openid-connect-identity-provider/
I am using python Boto3. This is my current code:
import boto3
id_token='aaaaaa.bbbbbbbb.cccccccc' #Got from the url salesforce sends after successful authentication
client = boto3.client('cognito-identity', 'us-east-2')
resp = client.get_id(AccountId='123456789123', IdentityPoolId='us-east-2:xxxxxxx-yyyy-zzz-hhhhh-jj888hhhh',
Logins={
'provider_url': id_token
}
)
Any help would be greatly appreciated.
Apparently the problem was the provider_url as the error suggested. I was simply putting login.salesforce.com when instead i had to change it to what the id_token was returning as their issuer id. I had to change it in the AWS access providers in IAM as well as in code.

Using AWS Cognito access token in requests for API gateway in Python

I've been able to obtain Access Token for my Aws Cognito user (using this).
But I can not figure out how generate an authenticated request with it for an Api Gateway with Cognito authorizer.
Can someone please share a sample snippet?
The problem should be in API Gateway and Cognito User Pool configuration. You could use id token instead of access token in header request and it should work if API Gateway and Cognito User Pool have a basic configuration.
If you prefer to use access token, you must check some details in configuration of API Gateway and Cognito User Pool: there shall be a
Resource Server in Cognito and
at the same time there shall be defined OAuth Scopes in Method Request of API Gateway coherently to Resource server.
You can find a good explanation about this configuration in this question:
AWS API Gateway - using Access Token with Cognito User Pool authorizer?
I suggest you this last way and to use access token.
About the request header, it's enough to put 'Authorization': YOUR_ACCESS_TOKEN.
Check to have added 'Authorizarion' in Token Source when you have created the Authorizer in API Gateway.
Put the access or id token obtained from the Cognito user pool in the Authentication header when making an API Gateway request
My answer assumes that you have Cognito Authorizer, not Lambda Authorizer. When you create the Cognito Authorizer, you give the name of the authorization token in the Token Source field. For example, auth_token.
To call the API resource to which the authorizer is screwed, you need the IdToken of the user who is currently logged in.
With the help of lambda functions, you can organize user login with obtaining IdToken, AccessToken, and RefreshToken. IdToken and AccessToken don't live long. You can make a lambda function that sends a RefreshToken to the User Pool and gets back the fresh IdToken in response. Read more about all this stuff here.
So, you have a fresh IdToken, as well as the name of the token that the Cognito authorizer requires. In my example, it is auth_token.
import requests
import json
CustomHeader = {'auth_token': "very-long-id-token"}
r = requests.post(
"https://xxx.execute-api.us-east-1.amazonaws.com/beta/user/function-for-auth-users-only",
data=json.dumps({"whatever_you_need": "your_value"}),
headers=CustomHeader)
print(r.json())

AWS Boto / Warrant library: SRP authentication and credentials error

I have been stuck on the following issue for quite some time now. Within Python I want users to retrieve a token based upon their username and password from the AWS cognito-identity-pool making use of srp authentication. With this token I want the users to upload data to s3.
This is part of the code I use (from the warrant library): https://github.com/capless/warrant
self.client = boto3.client('cognito-idp', region_name="us-east-1")
response = boto_client.initiate_auth(
AuthFlow='USER_SRP_AUTH',
AuthParameters=auth_params,
ClientId=self.client_id
)
def get_auth_params(self):
auth_params = {'USERNAME': self.username,
'SRP_A': long_to_hex(self.large_a_value)}
if self.client_secret is not None:
auth_params.update({
"SECRET_HASH":
self.get_secret_hash(self.username,self.client_id, self.client_secret)})
return auth_params
However, I keep on getting:
botocore\auth.py", line 352, in add_auth raise NoCredentialsError
botocore.exceptions.NoCredentialsError: Unable to locate credentials
I was able to get rid of this error by adding credentials in the .aws/credentials file. But this is not in line with the purpose of this program. It seems like there is a mistake in the warrant or botocore library and the it keeps on attempting to use the AWS Access Key ID and AWS Secret Access Key from the credentials file, rather than that the given credentials (username and password) are used.
Any help is appreciated
I am on to Cognito team. initiate auth is an unauthenticated call so it shouldn't require you to provide AWS credentials. The service endpoint will not validate the sigv4 signature for these calls.
That being said, some client libraries have certain peculiarities in the sense that you need to provide some dummy credentials otherwise the client library will throw an exception. However you can provide anything for the credentials.
I too ran into this, using warrant.
The problem is that the boto3 libraries are trying to sign the request to aws, but this request is not supposed to be signed. To prevent that, create the identity pool client with a config that specifies no signing.
import boto3
from botocore import UNSIGNED
from botocore.config import Config
client = boto3.client('cognito-idp', region_name='us-east-1', config=Config(signature_version=UNSIGNED))
AWS Access Key ID and AWS Secret Access Key are totally different from username and password.
The Boto3 client has to connect to the AWS service endpoint (in your case: cognito-idp.us-east-1.amazonaws.com) to execute any API. Before executing an API, the API credentials (key+secret) have to provided to authenticate your AWS account. Without autheticating your account, you cannot call cognito-idp APIs.
There is one AWS account (key/secret) but there can be multiple users (username/password).

Categories