我们有一个 Python 代码试图查询部署在 Microsoft Azure 上的 API。该代码首先使用
azure-identity
库从 API 请求访问令牌,然后在请求的 Authorization
标头中发送令牌,如下所示:
import requests
from azure.identity import ClientSecretCredential
TENANT_ID = 'my-tenant-id'
CLIENT_ID = 'my-client-id'
CLIENT_SECRET = "my_client-secret"
SCOPES = ['api://my-client-id/.default']
identity_client = ClientSecretCredential(tenant_id=TENANT_ID,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
authority='https://login.microsoftonline.com')
access_token = identity_client.get_token(SCOPES[0])
#Request the API endpoint
json = {
"function_name": "function_name",
"param1": "param1_value",
"param2": "param2_value",
}
headers = {
"Authorization": f"Bearer {access_token.token}",
"Content-Type": "application/json"
}
response = requests.get('https://myapi.whatever/myendpoint',
json=json, headers=headers)
if response.status_code == 200:
print(response.json()["result"])
else:
print(response)
但是,我们也获得了一个访问令牌(在
jwt.io
上具有有效签名);我们在查询端点时收到以下错误/响应:
{'_content': b'missing_claim',
'_content_consumed': True,
'_next': None,
'status_code': 401,
'headers': {'Date': 'Fri, 12 May 2023 15:25:27 GMT', 'Content-Type': 'text/plain', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'Request-Context': 'appId=cid-v1:752b04bc-08aa-4002-a618-d3e7be07a371', 'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'sameorigin', 'X-Permitted-Cross-Domain-Policies': 'none', 'Referrer-Policy': 'no-referrer'},
'raw': <urllib3.response.HTTPResponse at 0x2967109e3a0>,
'url': 'https://myapi.whatever/myendpoint',
'encoding': 'ISO-8859-1',
'history': [],
'reason': 'Unauthorized',
'cookies': <RequestsCookieJar[]>,
'elapsed': datetime.timedelta(microseconds=306335),
'request': <PreparedRequest [GET]>,
'connection': <requests.adapters.HTTPAdapter at 0x296710856a0>}
我不确定是什么原因造成的,我们也正确配置了 API 的权限......任何人都知道是什么导致了这个错误以及如何解决它?谢谢。
另请注意,我们尝试使用其他库,例如
msal
例如:
app = msal.ConfidentialClientApplication(
client_id=CLIENT_ID,
client_credential=[CLIENT_SECRET],
authority='https://login.microsoftonline.com/my-tenant-id',
token_cache=cache,
)
result = None
result = app.acquire_token_silent(scopes=SCOPES, account=None)
if not result:
print('Here')
result = app.acquire_token_for_client(scopes=SCOPES)
但还是一样的错误...
我同意 @Gaurav Mantri,如果您的服务主体没有调用 API 的权限,通常会出现错误
401 Unauthorized
。
为了确认,解码访问令牌并检查它是否具有
scp
API 权限声明。
如果您在
Expose an API
选项卡中添加范围,它会创建 delegated 权限,如下所示:
我使用 client credentials flow 像这样通过 Postman 生成访问令牌:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <clientappID>
client_secret: <secret>
scope: api://<webapi_appID>/.default
回复:
当我使用上面的访问令牌调用 API 时,我也遇到了
401 Unauthorized
错误,如下所示:
GET https://myapiendpoint/weatherforecast
回复:
请注意,客户端凭证流不适用于delegated 权限。因此,
声明将在生成的令牌中丢失 与客户端凭据流。scp
要解决错误,您必须使用 delegated 流,如授权代码流、交互流或用户名密码流等...
就我而言,我使用 授权码 流通过 Postman 生成访问令牌,如下所示:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:authorization_code
client_id: <clientappID>
client_secret: <secret>
scope: api://<webapi_appID>/.default
redirect_uri: https://jwt.ms
code: code
回复:
当我在 jwt.ms 中解码此令牌时,它具有
scp
声明,如下所示:
我可以像这样使用上面的访问令牌成功地进行API调用:
GET https://myapiendpoint/weatherforecast
回复:
您可以检查此SO线程以通过Python使用身份验证代码流生成访问令牌。