I'm currently working on a project where I need to access various URLs related to Microsoft Forms from my Python backend. However, I'm facing a 403 error when attempting to do so. Here's a breakdown of what I'm trying to achieve and the steps I've taken so far:
URLs to access: Retrieve all Forms for a Microsoft 365 Group:
https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms
Obtain details for a group form:
https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms('{formId}')
Fetch questions from a group form:
https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms('{formId}')/questions
Retrieve responses to a group form:
https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms('{formId}')/responses
Objective: Access URLs for Microsoft Forms data from backend.
Approach:
First, I log in to the MS Forms website.
Then, I append the necessary parameters (tenantId and groupId) to the URLs I want to access, and I am getting browser responses in JSON format.
Now using backend requests, I attempt to access these URLs by passing the required authentication token.
Issue: Despite successfully obtaining the authentication token using the provided code snippet, when I tried to access the Microsoft Forms URLs with the generated token, I received a 403 error.
Here's the code snippet I'm using to obtain the token:
token_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
data = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"scope": "https://forms.office.com/.default"
}
response = requests.post(token_url, data=data)
response.raise_for_status() # Raise an exception for non-200 status codes
And here's the code snippet I'm using to access the Microsoft Forms URLs with the obtained token:
headers = {
'Authorization': f'Bearer {response.json()["access_token"]}',
'Content-Type': 'application/json',
}
base_url = "https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms"
list_response = requests.get(url=base_url, headers=headers)
I'm uncertain why I'm receiving a 403 error despite having a valid token. I followed the instructions mentioned in this post, but was unsuccessful.
Any insights or assistance on resolving this issue would be highly appreciated.
Thank you in advance!
Initially, I too got same error when I used token generated with client credentials flow to call MS Forms API like this:
import requests
tenant_id = "tenantId"
client_id = "appID"
client_secret = "secret"
token_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
data = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"scope": "https://forms.office.com/.default"
}
response = requests.post(token_url, data=data)
headers = {
'Authorization': f'Bearer {response.json()["access_token"]}',
'Content-Type': 'application/json',
}
base_url = "https://forms.office.com/formapi/api/tenantId/groups/groupId/forms"
list_response = requests.get(url=base_url, headers=headers)
print(list_response)
Response:
To resolve the error, you need to switch to delegated flows for generating bearer token that requires user to login at least once.
In my case, I ran below CLI command to connect with Azure account initially in terminal like this:
az login --tenant tenantID
Now, I ran below sample Python code that uses azure-identity
module by generating token with AzureCliCredential() method and got forms details in response successfully:
import requests
from azure.identity import DefaultAzureCredential, InteractiveBrowserCredential, AzureCliCredential
# Use one of these credential objects to get an access token
cred = AzureCliCredential() # i.e. `az login`
# cred = InteractiveBrowserCredential()
# cred = DefaultAzureCredential()
# Request an access token with the following scope
scope = "https://forms.office.com/.default"
token = cred.get_token(scope)
tenantId = "tenantId"
groupId = "groupId"
url = f"https://forms.office.com/formapi/api/{tenantId}/groups/{groupId}/forms"
# Provide the access token in the request header
headers = {"Authorization": f"Bearer {token.token}"}
list_response = requests.get(url, headers=headers)
print(list_response.json())
Response:
Reference:
How to have access to an online MS Form responses with code? - Stack Overflow by 9nut