TypeError: 'str' object is not callable`ClientContext
Hi,
I am trying to access sharepoint sites using msal.
site_url = "https://organization.sharepoint.com/sites/mysite"
tenant_id = "xxxx-xxxx-xxxx"
client_id = "xxxx-xxxx-xxxx"
client_secret = "xxxx-xxxx-xxxx"
authority_url = f"https://login.microsoftonline.com/{tenant_id}"
app = msal.ConfidentialClientApplication(
client_id,
authority=authority_url,
client_credential=client_secret
)
scope = ['https://graph.microsoft.com/.default']
result = app.acquire_token_for_client(scopes=scope)
print(result)
if "access_token" in result:
print("Authentication successful")
token = result['access_token']
print(f"Token type: {type(token)}, Token: {token}")
context = ClientContext(site_url).with_access_token(token)
site = context.web
context.load(site)
context.execute_query()
I am getting the access token correctly but it throws following error while running the query on the context:
context.execute_query() File "/opt/conda/lib/python3.10/site-packages/office365/runtime/client_runtime_context.py", line 173, in execute_query self.pending_request().execute_query(qry) File "/opt/conda/lib/python3.10/site-packages/office365/runtime/client_request.py", line 37, in execute_query response = self.execute_request_direct(request) File "/opt/conda/lib/python3.10/site-packages/office365/runtime/client_request.py", line 46, in execute_request_direct self.beforeExecute.notify(request) File "/opt/conda/lib/python3.10/site-packages/office365/runtime/types/event_handler.py", line 41, in notify listener(*args, **kwargs) File "/opt/conda/lib/python3.10/site-packages/office365/sharepoint/client_context.py", line 283, in _authenticate_request self.authentication_context.authenticate_request(request) File "/opt/conda/lib/python3.10/site-packages/office365/runtime/auth/authentication_context.py", line 249, in authenticate_request self._authenticate(request) File "/opt/conda/lib/python3.10/site-packages/office365/runtime/auth/authentication_context.py", line 168, in _authenticate self._cached_token = token_func() TypeError: 'str' object is not callable
As far my understanding, all required permissions to the app are granted through AAD portal
Am I missing something? Or any other permissions should be added? Pleas help
I'm hitting the same error. Using Office365-REST-Python-Client 2.5.11 (and msal 1.30.0).
I could make it work in my particular Python script using SharePoint Online tenant_id and client_id combined with browser authentication by making the two changes below in .../site-packages/office365/runtime/auth/authentication_context.py However, I have no idea if this would impact anything else.
def _get_authorization_header(token):
# type: (Any) -> str
return "{token_type} {access_token}".format(
#ez20240808
#token_type=token.tokenType, access_token=token.accessToken
token_type="Bearer", access_token=token
)
def _authenticate(request):
if self._cached_token is None:
#ez20240808
#self._cached_token = token_func()
self._cached_token = token_func
request.set_header(
"Authorization", _get_authorization_header(self._cached_token)
)
The error occurs because with_access_token expects a function that returns the token, not the token string itself. This is necessary because with_access_token is designed to call the function dynamically to retrieve a fresh token when needed, which is important in OAuth2 flows where tokens can expire and need to be refreshed.
How to Fix the Issue Instead of passing the token string directly, you should pass a function that returns the token. Here’s an example of how you can do this:
from dataclasses import dataclass
@dataclass
class Token:
tokenType: str
accessToken: str
# Function to retrieve the access token
def get_token():
token_response = app.acquire_token_for_client(scopes=scopes)
if "access_token" in token_response:
return Token(tokenType="Bearer", accessToken=token_response["access_token"])
else:
raise Exception(
f"Authentication error: {token_response.get('error')}, {token_response.get('error_description')}"
)
# Use the get_token function in with_access_token
ctx = ClientContext(site_url).with_access_token(get_token)
Additionally, note that my Token class is mandatory because there is an error in the code that formats the token using the function:
def _get_authorization_header(token):
# type: (Any) -> str
return "{token_type} {access_token}".format(
token_type=token.tokenType, access_token=token.accessToken
)
This function expects a token as a dictionary but uses attribute accessors (.tokenType, .accessToken) instead of dict.get(). To resolve this, I introduced a Token class with attributes to handle the formatting correctly.
@pdakwal
In the version I'm using Office365-REST-Python-Client==2.5.14, the with_access_token takes a function that returns a string, not a plain string. I believe that helps with keeping expiring tokens up to date.
def with_access_token(self, token_func):
# type: (Callable[[], TokenResponse]) -> Self
To make that work:
From the example above
from dataclasses import dataclass
@dataclass
class Token:
tokenType: str
accessToken: str
# Function to retrieve the access token
def wrap_token(token_response : dict) -> Token:
if "access_token" in token_response:
return Token(tokenType="Bearer", accessToken=token_response["access_token"])
else:
raise Exception(
f"Authentication error: {token_response.get('error')}, {token_response.get('error_description')}"
)
Then:
get_token_func = lambda: wrap_token(token_result)
return ClientContext(site_url).with_access_token(get_token_func)