Trying to create Azure B2C user with azure-graphrbac - python

We are trying to import our existing users into our B2C tenant. For this, we have been trying to use the azure-graphrbac python library.
I have followed this guide to register an application to be used with the graph api.
I'm using the below code to try and create a user:
from azure.graphrbac import GraphRbacManagementClient
from azure.common.credentials import ServicePrincipalCredentials
from azure.graphrbac.models import UserCreateParameters, PasswordProfile
credentials = ServicePrincipalCredentials(
client_id="<CLIENT ID>",
secret="<SECRET>",
tenant="<TENANT ID>"
)
tenant_id = '<myb2ctenant>.onmicrosoft.com'
graphrbac_client = GraphRbacManagementClient(
credentials,
tenant_id
)
ucp = UserCreateParameters(
user_principal_name="my#mail.com",
account_enabled=True,
display_name='Martin T',
mail_nickname='<mymail>',
additional_properties={
"signInNames": [{"type": "emailAddress", "value": "<mymail>"}]
},
user_type="LocalAccount",
password_profile=PasswordProfile(
password='<somepassword>',
force_change_password_next_login=True
)
)
user = graphrbac_client.users.create(ucp)
I've made sure that the client id, secret and tenant id are correct. However, I keep getting this error:
GraphErrorException: Access Token missing or malformed.
Does anyone have an idea as to what I might be doing wrong?

As Laurent said, you need define resource. The default resource is https://management.core.windows.net/. In your scenario, you want to create a user, the resource is https://graph.windows.net.
Your code also has some mistake, I modify it. The following code works for me.
from azure.graphrbac import GraphRbacManagementClient
from azure.common.credentials import ServicePrincipalCredentials
from azure.graphrbac.models import UserCreateParameters, PasswordProfile
credentials = ServicePrincipalCredentials(
client_id="",
secret="",
resource="https://graph.windows.net",
tenant = ''
)
tenant_id = ''
graphrbac_client = GraphRbacManagementClient(
credentials,
tenant_id
)
ucp = UserCreateParameters(
user_principal_name="",
account_enabled=True,
display_name='Martin T',
##I test in my lab, if I use this line, I will get error log and could not create a user.
#additional_properties={
# "signInNames": [{"type": "emailAddress", "value": ""}]
#},
##user_type only support Member or Guest, see this link https://learn.microsoft.com/en-us/python/api/azure.graphrbac.models.usercreateparameters?view=azure-python
user_type="Member",
mail_nickname = 'shuitest',
password_profile=PasswordProfile(
password='',
force_change_password_next_login=True
)
)
user = graphrbac_client.users.create(ucp)
See SDK in this link.

You service principal authentication needs to define "resource":
https://learn.microsoft.com/en-us/python/api/overview/azure/activedirectory
credentials = UserPassCredentials(
'user#domain.com', # Your user
'my_password', # Your password
resource="https://graph.windows.net"
)

Related

Deploy resource in Azure with python

I am working on deploy resources in Azure using python based on provided templates. As a starting point I am working with https://github.com/Azure-Samples/Hybrid-Resource-Manager-Python-Template-Deployment
Using as it is, I am having an issue at the beginning of the deployment (deployer.py deploy function)
def deploy(self, template, parameters):
"""Deploy the template to a resource group."""
self.client.resource_groups.create_or_update(
self.resourceGroup,
{
'location': os.environ['AZURE_RESOURCE_LOCATION']
}
)
The error message is
Message='ServicePrincipalCredentials' object has no attribute
'get_token'
The statement is correct, ServicePrincipalCredentials get_token attribute doesn't exist, however token is, may be an error due to an outdated version?
Based on the constructor information, the error may be on credentials creation or client creation
def __init__(self, subscription_id, resource_group, pub_ssh_key_path='~/id_rsa.pub'):
mystack_cloud = get_cloud_from_metadata_endpoint(
os.environ['ARM_ENDPOINT'])
subscription_id = os.environ['AZURE_SUBSCRIPTION_ID'] //This may be an error as subscription_id is already provided as a parameter
credentials = ServicePrincipalCredentials(
client_id=os.environ['AZURE_CLIENT_ID'],
secret=os.environ['AZURE_CLIENT_SECRET'],
tenant=os.environ['AZURE_TENANT_ID'],
cloud_environment=mystack_cloud
) --> here
self.subscription_id = subscription_id
self.resource_group = resource_group
self.dns_label_prefix = self.name_generator.haikunate()
pub_ssh_key_path = os.path.expanduser(pub_ssh_key_path)
# Will raise if file not exists or not enough permission
with open(pub_ssh_key_path, 'r') as pub_ssh_file_fd:
self.pub_ssh_key = pub_ssh_file_fd.read()
self.credentials = credentials
self.client = ResourceManagementClient(self.credentials, self.subscription_id,
base_url=mystack_cloud.endpoints.resource_manager) --> here
Do you know how I can fix this?
After struggling a little, I could find a solution. Just replace
credentials = ServicePrincipalCredentials(
client_id=os.environ['AZURE_CLIENT_ID'],
secret=os.environ['AZURE_CLIENT_SECRET'],
tenant=os.environ['AZURE_TENANT_ID'],
cloud_environment=mystack_cloud
)
for
self.credentials = DefaultAzureCredential()
The final code looks like:
from azure.identity import DefaultAzureCredential
def __init__(self, subscriptionId, resourceGroup):
endpoints = get_cloud_from_metadata_endpoint(os.environ.get("ARM_ENDPOINT"))
self.subscriptionId = subscriptionId
self.resourceGroup = resourceGroup
self.credentials = DefaultAzureCredential()
self.client = ResourceManagementClient(self.credentials, self.subscriptionId,
base_url=endpoints.endpoints.resource_manager)

How to patch an existing application using Python Azure SDK and Graph?

I am trying to add a reply_url programmatically to an Azure app registration, but I receive an azure.graphrbac.models.graph_error_py3.GraphErrorException: Specified HTTP method is not allowed for the request target.
It fails when I try to update an existing application with new reply_urls.
SDK I am using is: azure-graphrbac==0.61.1
My code:
from azure.common.credentials import ServicePrincipalCredentials
from azure.graphrbac import GraphRbacManagementClient
from azure.graphrbac.models import ApplicationUpdateParameters
class GraphClient:
def __init__(self, client_id, client_secret, tenant_id, object_id):
self._credentials = ServicePrincipalCredentials(
client_id=client_id,
secret=client_secret,
tenant=tenant_id,
resource="https://graph.windows.net"
)
self._graph_client = GraphRbacManagementClient(
credentials=self._credentials,
tenant_id=tenant_id
)
self._object_id = object_id
self._application = self._graph_client.applications.get(self._object_id)
def get_reply_urls(self) -> List[str]:
return self._application.reply_urls
def add_reply_url(self, reply_url) -> None:
reply_urls: list = self.get_reply_urls()
self._graph_client.applications.patch(
self._object_id,
ApplicationUpdateParameters(
reply_urls=[
*reply_urls,
reply_url]
)
)
Could not reproduce your issue, use the same version of azure-graphrbac, I test your code on my side, it works fine.
testclient = GraphClient(client_id = "xxxxx",client_secret = "xxxxx", tenant_id = "xxxxx", object_id = "xxxxx")
testclient.add_reply_url(reply_url = "http://localhost:8085")
Check in the portal:
Also, I test the sdk directly, both work.
from azure.common.credentials import ServicePrincipalCredentials
from azure.graphrbac import GraphRbacManagementClient
from azure.graphrbac.models import ApplicationUpdateParameters
_credentials = ServicePrincipalCredentials(
client_id="xxxxx",
secret="xxxxx",
tenant="xxxxx",
resource="https://graph.windows.net"
)
_graph_client = GraphRbacManagementClient(
credentials=_credentials,
tenant_id="xxxxx"
)
app = _graph_client.applications.patch(
application_object_id = "xxxxx",
parameters = ApplicationUpdateParameters(reply_urls = ["http://localhost:8080","http://localhost:8081"])
)
Update call looks good however it depends on a legacy API (AAD Graph) and tools. It's strongly recommended to move to MS Graph which supports almost all Azure AD Graph operations and will fully support them all in a future. Applications being one of them.
You can use Requests-OAuthlib or Microsoft Graph Python Client Library for this.

Azure key vault create in Python

I am trying to programmatically create a key vault in python using this tutorial (https://learn.microsoft.com/en-us/python/api/overview/azure/key-vault?view=azure-python).
No errors till the last step where it throws an exception when I call client.vaults.create_or_update() because I might not have used the right values for ALLOW_OBJECT_ID and ALLOW_TENANT_ID. The documentation says these values can be found on the portal but I could not find it, is there a way to get it programmatically?
Error:
srest.exceptions.AuthenticationError: , AdalError: Get Token request returned http error: 400 and server response: {"error":"unauthorized_client","error_description":"AADSTS700016: Application with identifier XXX was not found in the directory YY
Code:
import subprocess
import json
from azure.mgmt.keyvault import KeyVaultManagementClient
from azure.common.credentials import ServicePrincipalCredentials
def get_subscription():
subs = json.loads(subprocess.check_output('az account list',
shell=True).decode('utf-8'))
subscription = subs[1]['id']
cmd = 'az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/%s"' % subscription
creds = json.loads(subprocess.check_output(cmd, shell=True).decode('utf-8'))
return subscription, creds
def create_key_vault(vault_name='TestKeyVault'):
subscription_id, creds = get_subscription()
client_id = creds['appId']
secret = creds['password']
tenant = creds['tenant']
credentials = ServicePrincipalCredentials(client_id=client_id, secret=secret, tenant=tenant)
client = KeyVaultManagementClient(credentials, subscription_id)
ALLOW_OBJECT_ID = client_id
ALLOW_TENANT_ID = tenant
RESOURCE_GROUP = 'SomeRG'
VAULT_NAME = vault_name
# Vault properties may also be created by using the
# azure.mgmt.keyvault.models.VaultCreateOrUpdateParameters
# class, rather than a map.
operation = client.vaults.create_or_update(
RESOURCE_GROUP,
VAULT_NAME,
{
'location': 'eastus',
'properties': {
'sku': {
'name': 'standard'
},
'tenant_id': ALLOW_TENANT_ID,
'access_policies': [{
'object_id': ALLOW_OBJECT_ID,
'tenant_id': ALLOW_TENANT_ID,
'permissions': {
'keys': ['all'],
'secrets': ['all']
}
}]
}
}
)
vault = operation.result()
print(f'New vault URI: {vault.properties.vault_uri}')
Well, the objects could be the users, security groups, service principals in your Azure AD tenant, if you not familiar with access policy in keyvault, check this doc.
To get them grammatically, the easiest way in your case is to use Azure CLI in python.
Use az account show to get the tenantId.
Use az ad user list to get the objectId of the user.
Use az ad group list to get the objectId of the security group.
Use az ad sp list to get the objectId of the service principal.
Then you should specify the ALLOW_OBJECT_ID and ALLOW_TENANT_ID with the any objectId you need and tenantId above.

How to create a SECRET_HASH for AWS Cognito using boto3?

I want to create/calculate a SECRET_HASH for AWS Cognito using boto3 and python. This will be incorporated in to my fork of warrant.
I configured my cognito app client to use an app client secret. However, this broke the following code.
def renew_access_token(self):
"""
Sets a new access token on the User using the refresh token.
NOTE:
Does not work if "App client secret" is enabled. 'SECRET_HASH' is needed in AuthParameters.
'SECRET_HASH' requires HMAC calculations.
Does not work if "Device Tracking" is turned on.
https://stackoverflow.com/a/40875783/1783439
'DEVICE_KEY' is needed in AuthParameters. See AuthParameters section.
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html
"""
refresh_response = self.client.initiate_auth(
ClientId=self.client_id,
AuthFlow='REFRESH_TOKEN',
AuthParameters={
'REFRESH_TOKEN': self.refresh_token
# 'SECRET_HASH': How to generate this?
},
)
self._set_attributes(
refresh_response,
{
'access_token': refresh_response['AuthenticationResult']['AccessToken'],
'id_token': refresh_response['AuthenticationResult']['IdToken'],
'token_type': refresh_response['AuthenticationResult']['TokenType']
}
)
When I run this I receive the following exception:
botocore.errorfactory.NotAuthorizedException:
An error occurred (NotAuthorizedException) when calling the InitiateAuth operation:
Unable to verify secret hash for client <client id echoed here>.
This answer informed me that a SECRET_HASH is required to use the cognito client secret.
The aws API reference docs AuthParameters section states the following:
For REFRESH_TOKEN_AUTH/REFRESH_TOKEN: USERNAME (required), SECRET_HASH
(required if the app client is configured with a client secret),
REFRESH_TOKEN (required), DEVICE_KEY
The boto3 docs state that a SECRET_HASH is
A keyed-hash message authentication code (HMAC) calculated using the
secret key of a user pool client and username plus the client ID in
the message.
The docs explain what is needed, but not how to achieve this.
The below get_secret_hash method is a solution that I wrote in Python for a Cognito User Pool implementation, with example usage:
import boto3
import botocore
import hmac
import hashlib
import base64
class Cognito:
client_id = app.config.get('AWS_CLIENT_ID')
user_pool_id = app.config.get('AWS_USER_POOL_ID')
identity_pool_id = app.config.get('AWS_IDENTITY_POOL_ID')
client_secret = app.config.get('AWS_APP_CLIENT_SECRET')
# Public Keys used to verify tokens returned by Cognito:
# http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html#amazon-cognito-identity-user-pools-using-id-and-access-tokens-in-web-api
id_token_public_key = app.config.get('JWT_ID_TOKEN_PUB_KEY')
access_token_public_key = app.config.get('JWT_ACCESS_TOKEN_PUB_KEY')
def __get_client(self):
return boto3.client('cognito-idp')
def get_secret_hash(self, username):
# A keyed-hash message authentication code (HMAC) calculated using
# the secret key of a user pool client and username plus the client
# ID in the message.
message = username + self.client_id
dig = hmac.new(self.client_secret, msg=message.encode('UTF-8'),
digestmod=hashlib.sha256).digest()
return base64.b64encode(dig).decode()
# REQUIRES that `ADMIN_NO_SRP_AUTH` be enabled on Client App for User Pool
def login_user(self, username_or_alias, password):
try:
return self.__get_client().admin_initiate_auth(
UserPoolId=self.user_pool_id,
ClientId=self.client_id,
AuthFlow='ADMIN_NO_SRP_AUTH',
AuthParameters={
'USERNAME': username_or_alias,
'PASSWORD': password,
'SECRET_HASH': self.get_secret_hash(username_or_alias)
}
)
except botocore.exceptions.ClientError as e:
return e.response
I also got a TypeError when I tried the above solution. Here is the solution that worked for me:
import hmac
import hashlib
import base64
# Function used to calculate SecretHash value for a given client
def calculateSecretHash(client_id, client_secret, username):
key = bytes(client_secret, 'utf-8')
message = bytes(f'{username}{client_id}', 'utf-8')
return base64.b64encode(hmac.new(key, message, digestmod=hashlib.sha256).digest()).decode()
# Usage example
calculateSecretHash(client_id, client_secret, username)

How to use create tags Python Azure SDK?

I am trying to create tags in Azure 2.0.0rc2 using Python.
Following is the code i used:
def __update_tags(self):
username = 'user#xyz.com'
password = 'user#1234'
subscription_id = '478-ytehn-47ds5-784aa-4758a'
credentials = UserPassCredentials(username=username, password=password)
resource_client = ResourceManagementClient(credentials=credentials)
tag_operations = TagOperations(client=resource_client)
tag_operations.create_or_update_value(tag_name='key_1', tag_value='val_1')
On running this code i am getting error like:
if self.client.credentials.subscription_id is not None:
AttributeError: 'UserPassCredentials' object has no attribute 'subscription_id'
Anyone have idea to solve this issue.
In your code, subscription_id is specified but not used. You need the subscription_id when creating the resource_client. Please replace "resource_client = ResourceManagementClient(credentials=credentials)" with the code below:
resource_client = ResourceManagementClient(
ResourceManagementClientConfiguration(
credentials,
subscription_id
)
Check here for more information.
Update:
confirm import ResourceManagementClientConfiguration
According to the documents (Resource Management and Resource Management Authentication), as #forester123 said, summarize the code as below.
from azure.common.credentials import UserPassCredentials
from azure.mgmt.resource.resources import ResourceManagementClient, ResourceManagementClientConfiguration
username = 'user#xyz.com'
password = 'user#1234'
subscription_id = '478-ytehn-47ds5-784aa-4758a'
credentials = UserPassCredentials(username, password)
resource_client = ResourceManagementClient(
ResourceManagementClientConfiguration(
credentials,
subscription_id
)
)

Categories

Resources