Python Salesforce API Authentication - python-3.x

I am trying to automate creating tickets in Salesforce. For this, I am using API with Python. I have got the Client ID and Client secret for my registered python Application. I have read many questions and as per the security purpose, I do not want to use the "user-password" flow for my script. Is there any way that I can only use "CLIENT ID" and "CLIENT SECRET" to get the access token where I can pass this access token in bearer header for other calls
import requests
params = {
"grant_type": "client_credentials",
"client_id": client_id, # Consumer Key
"client_secret": client_secret, # Consumer Secret
}
r=requests.post("https://login.salesforce.com/services/oauth2/token", params=params)
access_token = r.json().get("access_token")
instance_url = r.json().get("instance_url")
print("Access Token:", access_token)

You'll always need a SF user account. There's no way to just make a backend system talk to SF system. Salesforce treats everybody as user so you need to waste an account for "integration user" - but in return you can control access to tables, columns, functionalities just like you control real humans' access. This goes all the way down to the underlying Oracle database and database user privileges.
Whether you use OAuth2 flows (including client secrets) or maybe some certificate-based authentication - there will be always some element of "username and password" required. Best you can do is to make sure your app doesn't need to see & store the password, instead showing normal SF login prompt and on successful login user is redirected to your app to continue with known session id...
There might be something you'll be able to automate more if your app and SF use same Single Sign-On but broadly speaking... You have to either let users login to SF via your app or create the tickets as some dedicated admin user (and then you store this user's credentials in your app)

Related

Search User Information Across different Microsoft Tenants

I want to be able to search for users across multiple tenants, and therefore my thoughts were to create a python script that runs on HTTP triggered Azure functions. This python script can authenticate to Microsoft Graph API for different tenants via service principals and then search for a user and return the data. is this a good idea or is there a better way of doing this?
Let's discuss on the achievement.
I find that one multi-tenant azure ad application is enough for querying users in different tenant through graph api. For example, there're 2 tenants, I created a multi-tenant application in azure ad app registration, after that I generated the client secret and add api permission of User.Read.All.
Now I have an app with its client id and secret in 'tenant_a'. Next, visit https://login.microsoftonline.com/{tenant_b}/adminconsent?client_id={client-id} in the browser, after sign in with the admin account in tenant_b, it will appear a 'permission' window to make consent the application have permission in tenant_b, after the consent, you will the the app created in tenant_a appears in the list of Enterprise applications in tenant_b.
Now we need to generate access token for different tenant to call graph api. It's necessary to generate access token for each tenant, because I tried to use common to replace the domain in the request(https://login.microsoftonline.com/common/oauth2/v2.0/token), it can generate access token successfully, but the token can't used in the api to query user information. The query user api needs user principal name as the input parameter. For example, I have a user which account is 'bob#tenant_b.onmicrosoft.com', use the account as the parameter is ok to get response, but if I use 'bob' as the parameter, it will return 'Resource xxx does not exist...'.
I'm not an expert in python, I only found a sample and tested successfully with it. Here's my code, it will execute loop query until the user be found. And if you wanna a function, you may create a http trigger base on it.
import sys
import json
import logging
import requests
import msal
config = json.load(open(sys.argv[1]))
authorityName = ["<tenant_a>.onmicrosoft.com","<tenant_b>.onmicrosoft.com"]
username = "userone#<tenant_a>.onmicrosoft.com"
for domainName in authorityName:
# Create a preferably long-lived app instance which maintains a token cache.
print("==============:"+config["authority"]+domainName)
app = msal.ConfidentialClientApplication(
"<client_id>", authority="https://login.microsoftonline.com/"+domainName,
client_credential="<client_secret>",
)
# The pattern to acquire a token looks like this.
result = None
# Firstly, looks up a token from cache
# Since we are looking for token for the current app, NOT for an end user,
# notice we give account parameter as None.
result = app.acquire_token_silent(["https://graph.microsoft.com/.default"], account=None)
if not result:
result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
if "access_token" in result:
print("access token===:"+result['access_token'])
# Calling graph using the access token
graph_data = requests.get( # Use token to call downstream service
"https://graph.microsoft.com/v1.0/users/"+username,
headers={'Authorization': 'Bearer ' + result['access_token']}, ).json()
if "error" in graph_data:
print("error===="+json.dumps(graph_data, indent=2))
else:
print(json.dumps(graph_data, indent=2))
break
else:
print(result.get("error"))
print(result.get("error_description"))
print(result.get("correlation_id"))

Background Access to microsoft Graph API

i'm developing a single page application for a costumer. I need to create a script that allows me to access to my app without user interaction. for example i press a button and the script automatically log me in (i know username and password of the user). The user don't need to see the window where i put username and password.
Is it possible? at the moment my login script is:
const authResult = await msalClient.loginPopup(msalRequest);
localStorage.setItem('msalAccount', authResult.account.username);
// Get the user's profile from Graph
user = await getUser();
// Save the profile in session
localStorage.setItem('graphUser', JSON.stringify(user));
if(accountAttivo!=""){
setActiveUser();
}
updatePage(Views.home);
Looks like you are trying to follow the ROPC flow as you are using username and password credentials in your script, access token must be fetched from AAD before we call getUser(). To fetch any details using graph api we need to have access token please go through the article which helps you more in understanding.
Let's say we wrote a function called getAccessToken() to fetch the token. Once the token fetched and is saved in the sessions, following code can be used in getUser() to fetch the user profile.
const options = {
authProvider,
};
const client = Client.init(options);
let res = await client.api('/me')
.get();
Calling both the functions getAccessToken() and getUser() in the code flow of the button click event should bypass the user interaction with the application to enter credentials.
NOTE: Microsoft does not recommend to user ROPC flow. This most scenarios, more secure alternatives are available and recommended.
This flow requires a very high degree of trust in application, and
carries risks which are not present in other flows. You should only
use this flow when other more secure flows can’t be used.

Unattended authorization into API - no longer possible?

A brief investigation shows that there once existed an authorization mode known as SOBO (for example, see docusign send on behalf functionality), which I find useful in a scenario when an application associates signing requests not with a logged-on user but with some other user. However I am unable to find any mention of it in current documentation; on the contrary, documentation clearly says only 3 grant types are supported, and all three involve impersonated user's actively expressing his consent. No way to just send user credentials, or, alternatively, have user express his consent just once on the docusign admin page, then reuse that consent for, say, one year or forever. Or some other way to avoid end user interaction.
Also, a requirement of using redirect URI to send back continuation data implies one has to either use on-premise solution or make one's application visible on the internet. No way to use public docusign service with on-premise application, right?
DocuSign is discouraging Legacy Header authentication because it requires your integration to hold on to the user's plain-text password. It also cannot support accounts that leverage Single-Sign On or Two-Factor Authentication. The long-term plan is likely to eventually retire it entirely, but there's no timeline for that. Documentation has been retired, but integrations that have used it in the past can still do so.
JWT auth is the equivalent replacement. With individual consent, a user grants the integration access once. Unless the user revokes that consent, the integration will be able to generate access tokens to act as the user indefinitely - JWT consent does not expire.
If you have an Organization with a Claimed Domain, an org admin can grant blanket consent to allow an integration to act as any user under that domain. If you'd like to grant consent to an integrator key owned by your organization, it's as simple as navigating to Org Admin > Applications > Authorize Application. Granting consent to a 3rd party app is similar to the Individual Consent workflow, but has extra scopes as documented here: https://developers.docusign.com/esign-rest-api/guides/authentication/obtaining-consent
Note that while JWT auth does require a redirect URI to be registered, an integration doesn't necessarily need to 'catch' the user after they've granted consent. While it would be recommended that the landing page trigger the user to move forward in the workflow, it's acceptable to point your redirect URI to https://www.example.com, grant consent, and then generate an access token.
I know this question has already been answered, but I'll post this answer here just in case someone still needs to do this. This method does not require user's consent. The below is Node.js / JS but can be easily translated into whatever language with the basics below.
// set default authentication for DocuSign; pulls data from this account
var auth = {
'Username': '(user email)',
'Password': '(user password)',
'IntegratorKey': '(api key found in admin)',
};
var options = {
'uri': 'https://www.docusign.net/restapi/v2/login_information',
'method': 'GET',
'body': '',
'headers': {
'Content-Type': 'application/json',
// turns the auth object into JSON
'X-DocuSign-Authentication': JSON.stringify(auth)
}
};
// send off your request using the options above
The above returns a response:
{
"loginAccounts": [
{
"name":"Your Company Name",
"accountId":"0000000",
"baseUrl":"https://{your_subdomain}.docusign.net/restapi/v2/accounts/0000000",
"isDefault":"true",
"userName":"User's Name",
"userId":"(36 character UUID)",
"email":"user#example.com",
"siteDescription":""
}
]
}
At this point, you can have to save the baseUrl and accountId that is returned. For the baseUrl, you only need to save the the sub-domain and domain url (https://{your_subdomain}.docusign.net), not the url paramters after that.
Now you can have enough information to make requests. The below example request pulls all the templates under this account.
var options = {
'uri': baseUri+'/accounts/'+accountId+'/templates',
'method': 'GET',
'body': '',
'headers': {
'Content-Type': 'application/json',
// turns the auth object into JSON
'X-DocuSign-Authentication': JSON.stringify(auth)
}
};
// send off your request using the options above

Generating AAD Token with UPN

I am trying to generate a token for a user with below code.
string apiResourceId = "11224320-66b9-4132-8953-9aa485f07004";
string clientId = "bc9869a0-2393-4e42-8c52-845071640ea8";
Uri redirectUri = new Uri("https://localhost:44335/");
string authority = string.Format("https://login.windows.net/{0}",
"rudderless.onmicrosoft.com");
var authContext = new AuthenticationContext(authority);
AuthenticationResult authenticationResult;
authenticationResult = await authContext.AcquireTokenAsync(apiResourceId, clientId,
redirectUri, new PlatformParameters(PromptBehavior.Auto, null));
I have been getting an error in AcquireTokenAsync call -
AADSTS70002: The request body must contain the following parameter:
'client_secret or client_assertion'. Trace ID:
a198696d-8377-40eb-8351-527a25183500 Correlation ID:
24d4b47d-67bf-46c0-a6b7-a248c434512e Timestamp: 2017-09-20 23:09:38Z
Why do I need a client_secret or client_assertion if I want to generate a token when a user is authenticated against a AAD? The type of Client I am using is "Web app /API". However when I am trying to use a Native client I get the token generated but API call to apResourceID is generating unauthorized error.
Few Questions I am seeking help on related to the scinario -
Why I need to provide client_secret when I am using user auth flow?
Why AcquireToken succeed when I change the client Type to Native?
Why the token generated through native client gives an Unauthorize error?
Is there a way for admin to consent on behalf of every user in AAD?
Why I need to provide client_secret when I am using user auth flow?
Web Apps and APIs are considered Confidential Clients. See here for a definition of the different Client Types in the OAuth 2 Specification. These kinds of client always need to use their client secret to authenticate, no matter the flow they are following.
Confidential clients are typically issued (or establish) a set of
client credentials used for authenticating with the authorization
server (e.g., password, public/private key pair).
Why AcquireToken succeed when I change the client Type to Native?
Native Client Applications are a subset of Public Clients. These are defined, in the specification as:
Clients incapable of maintaining the confidentiality of their
credentials (e.g., clients executing on the device used by the
resource owner, such as an installed native application or a web
browser-based application), and incapable of secure client
authentication via any other means.
Therefore, they do not have or need a client_secret to authenticate... but this also means they can only authenticate with user context, whereas a confidential client could authenticate without a user present (Client Credential Flow).
Why the token generated through native client gives an Unauthorize
error?
This is hard to answer without knowing more about the error and the call you are making that causes this error. You should provide more information about this scenario.
Is there a way for admin to consent on behalf of every user
in AAD?
Yes. In the new Azure Active Directory V2 Endpoint, we have an "Admin Consent Endpoint".
Using the older V1 endpoint, we have an &prompt=admin_consent query string which you can read about here.

Azure App Service Easy Auth

I have an Azure mobile backend set up with easy auth for facebook and google authentication and it works as expected.
Every time a user signs in with any of the supported providers, I want to be able to verify if it's a new user or not (e-mail not in database), without make an additional call from client. Is this possible?
Every time a user signs in with any of the supported providers, I want to be able to verify if it's a new user or not (e-mail not in database), without make an additional call from client. Is this possible?
As far as I know, we couldn't directly verify if it's a new user or not.
No matter you use server flow or client flow, easy auth will just return access token for the client to access the mobile backend resources, it will not check the user is new or old.
If you want to achieve this requirement, you need write your own logic.
You could write codes after the user login successfully.
For example, facebook login.
If you the use have login successfully,you could call GetAppServiceIdentityAsync extension method to get the login credentials, which include the access token needed to make requests against the Facebook Graph API.
// Get the credentials for the logged-in user.
var credentials =
await this.User
.GetAppServiceIdentityAsync<FacebookCredentials>(this.Request);
if (credentials.Provider == "Facebook")
{
// Create a query string with the Facebook access token.
var fbRequestUrl = "https://graph.facebook.com/me/feed?access_token="
+ credentials.AccessToken;
// Create an HttpClient request.
var client = new System.Net.Http.HttpClient();
// Request the current user info from Facebook.
var resp = await client.GetAsync(fbRequestUrl);
resp.EnsureSuccessStatusCode();
// Do something here with the Facebook user information.
var fbInfo = await resp.Content.ReadAsStringAsync();
}
Then you could check the database according to the user information.
More details about how to get user information in server side, you could refer to How to: Retrieve authenticated user information.

Resources