Azure sharepoint multi-factor authentication with python - python

I'm trying to use python to download an excel file that is hosted in a sharepoint which is part of the Microsoft Azure platform. The sharepoint is password protected, and I have an account and a password which I can use to login in via my browser,
In order to authenticate with a python script I followed the method suggested in: Sharepoint authentication with python.
Which uses the O365 rest python client library and goes as follows:
from office365.runtime.auth.authentication_context import AuthenticationContext
from office365.sharepoint.client_context import ClientContext
url = 'https://organization.sharepoint.com/sites/something/somepage.aspx'
username = 'userx#organization.com'
password = 'fakepass'
ctx_auth = AuthenticationContext(url)
if ctx_auth.acquire_token_for_user(username, password):
ctx = ClientContext(url, ctx_auth)
else:
print(ctx_auth.get_last_error())
But I'm getting an error message back:
An error occurred while retrieving token: AADSTS50076: Due to a configuration
change made by your administrator, or because you moved to a new location, you
must use multi-factor authentication to access ''.
I do connect to this account from multiple devices (browser), and just once I was required to use MFA to log in (SMS message). Is there a way to get around this? Note that I'm not the admin of the system.

The error message is pretty intuitive, user credentials auth is not supported when Multi-Factor Authentication (MFA) enabled.
To circumvent this error, SharePoint App-Only flow could be utilized instead (supported by Office365-REST-Python-Client library).
Setting up an app-only principal with tenant permissions section describes how to configure it, to summarize it consist of two steps:
register App principal (think of it as a "service account")
grant a permissions
Once app principal is created and consented, it could be utilized to access SharePoint resource as demonstrated below:
from office365.sharepoint.client_context import ClientContext
from office365.runtime.auth.client_credential import ClientCredential
site_url = 'https://contoso.sharepoint.com/'
app_principal = {
'client_id': '--client-id-goes-here--',
'client_secret': '--client-secret-goes-here--',
}
credentials = ClientCredential(app_principal['client_id'], app_principal['client_secret'])
ctx = ClientContext(url).with_credentials(credentials)
web = ctx.web
ctx.load(web)
ctx.execute_query()
print("Web site title: {0}".format(web.properties['Title']))
Here is an instruction on how to configure SharePoint App-Only flow:
Note: app principal registration operation(steps 1 through 5)
needs to be performed once per tenant. Although the operation for
granting permissions ( steps 6-9) could be applied either per tenant
or site collection:
permissions granted per site collection and requires a site collection administrator (in the provided instruction the permissions
are granter per site collection)
If you prefer to grant permissions on tenant level, visit tenant administration site instead, the URL must include -admin to access
the tenant administration site, for example,
https://{tenant}-admin.sharepoint.com/_layouts/15/appinv.aspx. That
operation requires a tenant administrator permissions
Steps:
Go to the appregnew.aspx page in your SharePoint Online site. For example, https://{tenant}.sharepoint.com/_layouts/15/appregnew.aspx.
On this page, click the Generate buttons next to the Client ID and Client Secret fields to generate their values.
Store the client ID and client secret securely as these credentials can be used to read or update all data in your SharePoint Online environment. You will also use them to configure the SharePoint Online connection in application.
Under Title, specify a title. For example, Python console. Under App Domain, specify localhost. Under Redirect URI, specify https://localhost.
Note: Sometimes, if you specify a actual domain, e.g. sharepoint.com domain in the App Domain and Redirect URI fields, instead of localhost, the error message An unexpected error has occurred might encounter. Check the appregnew.aspx page and make sure both fields include the proper localhost URI.
Click Create.
Go to the appinv.aspx page on the site collection. For example, https://example.sharepoint.com/_layouts/15/appinv.aspx to grant site-scoped permissions.
Specify your client ID in the App Id field and click Lookup to find your app.
To grant permissions to the app, copy the XML below to the App’s permission request XML field:
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl" />
</AppPermissionRequests>
Note: For tenant level scope, permission request XML looks as follows:
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />
</AppPermissionRequests>
Click Create.
On the confirmation dialog, click Trust It to grant the permissions.

Related

Sending automated emails using Gmail API with Python and OAuth authentication

I have a Python code that was using SendGrid API to send emails, but now I want to migrate to Google in order to send business emails. It also runs in Docker containers.
I followed Gmail Python Quickstart in order to use Gmail API in my Python code and the problem is that when trying to send email, it shows an authorization link in Docker logs in order to get token, etc.
Is there a way to complete authorization in the background without any further interaction or use an API key just like SendGrid to programmatically authenticate your application?
I am a service provider and want to send emails such as reset password links, confirmation code, etc. automatically; the code is deployed on a Linux host. I have access to workspace account, and I already have verified my domain.
The tutorial you are following is designed for an installed application. Hence the InstalledAppFlow.
It states it at the top of the file.
Authorization credentials for a desktop application. To learn how to create credentials for a desktop application, refer to Create credentials.
This means when your code runs it is going to pop up the consent screen on the machine the code is running on, in this instance Docker.
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
You need to create it using a web application so that your users can consent to your application accessing their data and their Gmail account.
Automated send emails service account option.
You were not clear as who you are sending emails for. As you are using send grid, it implies to me this is some kind of automated system. Which would mean that you are trying to send emails on behalf of a Gmail account that you control.
In that case you would most often want to use a service account. Service accounts allow for server-to-server interaction between Google APIs. However, service accounts will only work with Gmail if this is a Google workspace Gmail account and you can set up domain-wide delegation.
There is an example in the documentation. Just change it to Gmail scopes. The key point is the create_delegated which must be a user on your domain.
from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
# Email of the Service Account
SERVICE_ACCOUNT_EMAIL = '<some-id>#developer.gserviceaccount.com'
# Path to the Service Account's Private Key file
SERVICE_ACCOUNT_PKCS12_FILE_PATH = '/path/to/<public_key_fingerprint>-privatekey.p12'
def create_directory_service(user_email):
"""Build and returns an Admin SDK Directory service object authorized with the service accounts
that act on behalf of the given user.
Arguments:
user_email: The email of the user. Needs permissions to access the Admin APIs.
Returns:
Admin SDK directory service object.
"""
credentials = ServiceAccountCredentials.from_p12_keyfile(
SERVICE_ACCOUNT_EMAIL,
SERVICE_ACCOUNT_PKCS12_FILE_PATH,
'notasecret',
scopes=['https://www.googleapis.com/auth/admin.directory.user'])
credentials = credentials.create_delegated(user_email)
return build('admin', 'directory_v1', credentials=credentials)
Standard Gmail solution
You can run your application once, and then when you place it in the Docker container, make sure that you include the token.json file that was created this is the file that contains the credentials that grant the application access to your account.
If you open it you will find an access token and a refresh token within. The refresh token will give your application the ability to request a new access token whenever it needs one.

Spotipy can't authenticate on Azure Functions (OAuth)

I made a script that works perfectly when it runs locally but isn't able to authenticate in my Azure Functions app. At first, I thought it was because it couldn't read the .cache file.
After looking at the logs it's because it can't open a browser window to create the authentication token. I'm fairly new to Azure functions so I'm not sure how I can enable opening a browser if that's even possible.
I looked into the oauth2.py file in spotipy and found it's optional to open the browser but instead, the console asks for the redirected URL. Is there a way for me to get the redirected URL and enter it into the console instead?
Here's my code:
def create_playlist(cred):
"""Creates the playlist for Discover weekly to be copied to"""
# Gain authorization to create playlist
logging.info('Authenticating spotify secrets to create new playlist...')
# spotipy.CacheFileHandler(cache_path='')
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id=cred[0],
client_secret=cred[1],
redirect_uri=cred[2],
scope='playlist-modify-private',
open_browser=False,
))
# Get returned list from get_playlist_info function
logging.info('Determining playlist descriptors...')
info = get_playlist_info()
# New playlist for Discover Weekly
logging.info('Creating new playlist...')
new_playlist = sp.user_playlist_create(user=cred[4],
name=info[0],
public=False,
collaborative=False,
description=info[1])
logging.info('Returning new playlist.')
return new_playlist
In Redirect URIs you enters one or more addresses which you want to whitelist with Spotify. And this URI enables the Spotify authentication service to automatically re-launch your app every time the user logs in.
You can set your Redirect URL by navigating to your Spotify developer dashboard and open the project you are working on. Then click "edit settings" and look for the redirect URIs field and put your redirect URI in the field and lastly save it.
The Authorization Code Flow needs you to add a redirect URI to your application at My Dashboard. The redirect_uri argument or SPOTIPY_REDIRECT_URI environment variable must match the redirect URI added to your application in your Dashboard.
Following are some examples that you can use as redirect URIs.
http://example.com, http://localhost or http://127.0.0.1:9090
The correct way in which you should authenticate yourself is shown in the code snippet below.
token = util.prompt_for_user_token(
username=USERNAME,
scope=SCOPE,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
redirect_uri=REDIRECT_URI)
You should also export your redirect_uri, client ID, and secret before running your script and trying to authenticate. If you don't pass the values directly to the util.prompt_for_user_token function, it will read them from the environment.
Try running these in your terminal before running your script.
export SPOTIPY_CLIENT_ID="YOUR CLIENT ID"
export SPOTIPY_CLIENT_SECRET="YOUR CLIENT SECRET"
export SPOTIPY_REDIRECT_URI='http://localhost/'
For more information you can read this Authorization Code Flow document for Spotipy.

Accessing Office365 Sharepoint REST EndPoints using Python (Office365 Sharepoint REST client for Python)

I am trying to get access to a Sharepoint site from Python and it seems like AADSTS has a conditional access policy that’s preventing me from getting a token.
Has anyone used the Office365 Python Rest Client to successful connect to a Sharepoint Rest Endpoint?
This is the actual error I am seeing
"C:\Python\envs\dev\python.exe c:\Users\xxxxxx.vscode\extensions\ms-python.python-2020.8.105369\pythonFiles\lib\python\debugpy\launcher 52801 -- c:\xxxx\Code\PythonSharepointUpdate\readsharepoint.py " An error occurred while retrieving token from XML response: AADSTS53003: Access has been blocked by Conditional Access policies. The access policy does not allow token issuance. An error occurred while retrieving auth cookies from https://microsoft.sharepoint.com/_vti_bin/idcrl.svc/
I am trying to execute this code from the Sample
from office365.runtime.auth.user_credential import UserCredential
from office365.sharepoint.client_context import ClientContext
from office365.runtime.auth.authentication_context import AuthenticationContext
site_url = 'https://microsoft.sharepoint.com/teams/'
ctx = ClientContext(site_url).with_credentials(UserCredential("user#domain.com", "My Complex Password”))
web = ctx.web
ctx.load(web)
ctx.execute_query()
print("Web title: {0}".format(web.properties['Title']))
Also – is there a better way to authenticate to Sharepoint – I hate to have to type my Password in clear text in code ☹
Has anyone use ClientCredential? Is there a seperate setup on the Sharepoint Site to enable ClientCredential? Does our Sharepoint let us do that?
The trick is to use ClientCredentials and not UserCredentials.
One will have to create a new app on the Sharepoint site with a new CliendID and Client Secret. After creating a new app, the newly created app has to be given permissions on the site. Once the permissions are set, you will have to Trust the app.
The full explanation and the steps are documented here https://github.com/vgrem/Office365-REST-Python-Client/wiki/How-to-connect-to-SharePoint-Online-and-and-SharePoint-2013-2016-2019-on-premises--with-app-principal

Access denied for service account when accessing IAP-protected application

Trying to let a python command line to access(HTTP GET) IAP-protected application.
With https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/iap/make_iap_request.py
But 403 err occurs Exception: Service account xxx#xxxxxxxxx-production.iam.gserviceaccount.com does not have permission to access the IAP-protected application.
Following methods have been tried, but still issue persists as above
1. remove/create the service account
2. re-create the json key file
3. grant the service account project owner permission
if __name__ == '__main__':
os.environ[
"GOOGLE_APPLICATION_CREDENTIALS"] = '/Users/foo/bar.json'
print make_iap_request('https://foo.com/', 'xxxxtheidforthatiapservice.apps.googleusercontent.com')
I think you need to follow the Authenticating from a service account procedure:
Use an OpenID Connect (OIDC) token to authenticate a service account
to a Cloud IAP-secured resource.
Add the service account to the access list for the Cloud IAP-secured project.
Generate a JWT-based access token. This uses a target_audience additional claim that requires a client ID. To find
your client ID, follow the steps below:
a. Go to the Cloud IAP page.
b. Find the resource you want to access, then click More > Edit
OAuth Client.
edit OAuth client on the More menu
c. On the Credentials page that appears, note the client ID.
Request an OIDC token for the Cloud IAP-secured client ID.
Include the OIDC token in an Authorization: Bearer header to make the authenticated request to the Cloud IAP-secured resource.
I faced the same problem.
You have to run the script authenticated as a service account used to access the IAP-protected application programmatically. You can do so either by running script directly from VM which uses that service account as default, or you will need to download SA credentials and do it locally Obtaining an OIDC token from a local service account key file
Besides that, you also need to assign the IAP-secured Web App User role to the desired Service Account for that particular IAP-protected app.
After that, rerun the script.

Python Google CustomerLicense "Not authorized to access the application ID"

I am trying to check if the user has installed my marketplace app
from oauth2client.client import SignedJwtAssertionCredentials
import json
from httplib2 import Http
from apiclient.discovery import build
fname = 'my-creds.json'
scope = [
'https://www.googleapis.com/auth/appsmarketplace.license'
]
applicationId='12345678'
customerId='mydomain.com'
json_key = json.load(open(fname))
credentials = SignedJwtAssertionCredentials(json_key['client_email'], json_key['private_key'], scope)
http_auth = credentials.authorize(Http())
appsmarket = build('appsmarket', 'v2', http=http_auth)
response = appsmarket.customerLicense().get(applicationId=applicationId, customerId=customerId).execute()
print response
I have created a service account in the same project as my app in Google console.
But I keep getting
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/appsmarket/v2/customerLicense/471879773528/ashish%40noodletools.com?alt=json returned "Not authorized to access the application ID">
Do you add service account to your domain's authorized API clients in security section?
Look at Delegating domain-wide authority to the service account here: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
To delegate domain-wide authority to a service account, an administrator of the Google Apps domain must complete the following steps:
Go to your Google Apps domain’s Admin console.
Select Security from the list of controls. If you don't see Security listed, select More controls from the gray bar at the bottom of the page, then select Security from the list of controls. If you can't see the controls, make sure you're signed in as an administrator for the domain.
Select Show more and then Advanced settings from the list of options.
Select Manage API client access in the Authentication section.
In the Client Name field enter the service account's Client ID.
In the One or More API Scopes field enter the list of scopes that your application should be granted access to.
Click Authorize.
Your application now has the authority to make API calls as users in your domain (to "impersonate" users). When you prepare to make authorized API calls, you specify the user to impersonate.

Categories