使用 python 的 Azure Function App:如何访问用户组进行授权
Azure Function App using python: How to access user groups for authorization
我对 Azure Function Apps 和 OAuth 还很陌生,所以请多多包涵。
我的设置
我有一个带有简单 python-function 的 Azure Function App,除了打印请求 headers:
什么都不做
import logging
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
aadIdToken = req.headers.get('X-MS-TOKEN-AAD-ID-TOKEN')
aadAccessToken = req.headers.get('X-MS-TOKEN-AAD-ACCESS-TOKEN')
principalID = req.headers.get('X-MS-CLIENT-PRINCIPAL-ID')
principalName = req.headers.get('X-MS-CLIENT-PRINCIPAL-NAME')
idProviderId = req.headers.get('X-MS-CLIENT-PRINCIPAL-IDP')
aadRefreshToken = req.headers.get('X-MS-TOKEN-AAD-REFRESH-TOKEN')
clientPrincipal = req.headers.get('X-MS-CLIENT-PRINCIPAL')
result = "\n"
myDict = sorted(dict(req.headers))
for key in myDict:
result += f"{key} = {dict(req.headers)[key]}\n"
return func.HttpResponse(
f"Hello, {name}. How are you ? Doing well ?"\
f"\n\nHere is some data concerning your Client principal:"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-ID: {principalID}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-NAME: {principalName}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-IDP: {idProviderId}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL: {clientPrincipal}"\
f"\n\nHere is some data concerning your AAD-token:"\
f"\nThis is your X-MS-TOKEN-AAD-ID-TOKEN: {aadIdToken}"\
f"\nThis is your X-MS-TOKEN-AAD-ACCESS-TOKEN: {aadAccessToken}"\
f"\nThis is your X-MS-TOKEN-AAD-REFRESH-TOKEN: {aadRefreshToken}"\
f"\n\n\nresult: {result}"\
)
else:
return func.HttpResponse(
"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
status_code=200
)
我按照这个 guide 让用户在调用函数之前通过 EasyAuth 进行身份验证。
这似乎工作正常。通过浏览器访问该功能时,我被重定向到 sign-in。成功 sign-in 后,我再次被重定向,HTTP 响应在浏览器中打印出来。由于我能够访问 X-MS-CLIENT-PRINCIPAL-ID
和 X-MS-CLIENT-PRINCIPAL-NAME
我想身份验证是成功的。但是,当打印出整个请求 header 时,我没有找到 X-MS-TOKEN-AAD-REFRESH-TOKEN
、X-MS-TOKEN-AAD-ACCESS-TOKEN
或 X-MS-TOKEN-AAD-ID-TOKEN
.
这是输出(输出太大;在屏幕截图中显示的输出下方我可以看到 header 内容):
First half of my output
我的问题
我现在要做的是通过函数的 python 代码访问分配给 logged-in 用户的组,以进一步授权他的请求(例如“用户只能执行指定组 xyz 时的功能,否则将提示他 'not allowed'").
为此,我在我的应用程序注册的令牌配置中添加了“组”声明。
据我所知,使用 ClaimsPrinciple object (source).
可以很容易地通过 .NET 中编码的函数访问用户组。
我如何才能通过 python 代码访问用户分配的组?
这可能吗?
我是不是理解错了?
跟进:
我现在不明白的一件事是,当我第一次通过浏览器访问该功能时(触发登录),我可以在 browser-debuggger 的 callback-http-request 中看到一个 id_token
):
Browser debugger: id_token in callback-request
当我使用 jwt.io 解密该令牌时,我能够看到分配的用户组的一些 ID,这似乎正是我想通过 python 代码访问的内容。
Re-loading 页面(我想请求然后使用已经过身份验证的浏览器 session)使回调消失。
header X-MS-CLIENT-PRINCIPAL
包含与 id_token 相同的声明。所以如果我们想得到group claim,我们可以base64解码header.
例如
我的代码
import logging
import azure.functions as func
import base64
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
aadAccessToken = req.headers.get('X-MS-TOKEN-AAD-ACCESS-TOKEN')
principalID = req.headers.get('X-MS-CLIENT-PRINCIPAL-ID')
principalName = req.headers.get('X-MS-CLIENT-PRINCIPAL-NAME')
idProviderId = req.headers.get('X-MS-CLIENT-PRINCIPAL-IDP')
aadRefreshToken = req.headers.get('X-MS-TOKEN-AAD-REFRESH-TOKEN')
clientPrincipal = req.headers.get('X-MS-CLIENT-PRINCIPAL')
clientPrincipal= base64.b64decode(clientPrincipal)
result = "\n"
myDict = sorted(dict(req.headers))
for key in myDict:
result += f"{key} = {dict(req.headers)[key]}\n"
return func.HttpResponse(
f"Hello, {name}. How are you ? Doing well ?"\
f"\n\nHere is some data concerning your Client principal:"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-ID: {principalID}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-NAME: {principalName}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-IDP: {idProviderId}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL: {clientPrincipal}"\
f"\n\nHere is some data concerning your AAD-token:"\
f"\nThis is your X-MS-TOKEN-AAD-ID-TOKEN: {aadIdToken}"\
f"\nThis is your X-MS-TOKEN-AAD-ACCESS-TOKEN: {aadAccessToken}"\
f"\nThis is your X-MS-TOKEN-AAD-REFRESH-TOKEN: {aadRefreshToken}"\
f"\n\n\nresult: {result}"\
)
else:
return func.HttpResponse(
"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
status_code=200
)
我对 Azure Function Apps 和 OAuth 还很陌生,所以请多多包涵。
我的设置
我有一个带有简单 python-function 的 Azure Function App,除了打印请求 headers:
什么都不做import logging
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
aadIdToken = req.headers.get('X-MS-TOKEN-AAD-ID-TOKEN')
aadAccessToken = req.headers.get('X-MS-TOKEN-AAD-ACCESS-TOKEN')
principalID = req.headers.get('X-MS-CLIENT-PRINCIPAL-ID')
principalName = req.headers.get('X-MS-CLIENT-PRINCIPAL-NAME')
idProviderId = req.headers.get('X-MS-CLIENT-PRINCIPAL-IDP')
aadRefreshToken = req.headers.get('X-MS-TOKEN-AAD-REFRESH-TOKEN')
clientPrincipal = req.headers.get('X-MS-CLIENT-PRINCIPAL')
result = "\n"
myDict = sorted(dict(req.headers))
for key in myDict:
result += f"{key} = {dict(req.headers)[key]}\n"
return func.HttpResponse(
f"Hello, {name}. How are you ? Doing well ?"\
f"\n\nHere is some data concerning your Client principal:"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-ID: {principalID}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-NAME: {principalName}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-IDP: {idProviderId}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL: {clientPrincipal}"\
f"\n\nHere is some data concerning your AAD-token:"\
f"\nThis is your X-MS-TOKEN-AAD-ID-TOKEN: {aadIdToken}"\
f"\nThis is your X-MS-TOKEN-AAD-ACCESS-TOKEN: {aadAccessToken}"\
f"\nThis is your X-MS-TOKEN-AAD-REFRESH-TOKEN: {aadRefreshToken}"\
f"\n\n\nresult: {result}"\
)
else:
return func.HttpResponse(
"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
status_code=200
)
我按照这个 guide 让用户在调用函数之前通过 EasyAuth 进行身份验证。
这似乎工作正常。通过浏览器访问该功能时,我被重定向到 sign-in。成功 sign-in 后,我再次被重定向,HTTP 响应在浏览器中打印出来。由于我能够访问 X-MS-CLIENT-PRINCIPAL-ID
和 X-MS-CLIENT-PRINCIPAL-NAME
我想身份验证是成功的。但是,当打印出整个请求 header 时,我没有找到 X-MS-TOKEN-AAD-REFRESH-TOKEN
、X-MS-TOKEN-AAD-ACCESS-TOKEN
或 X-MS-TOKEN-AAD-ID-TOKEN
.
这是输出(输出太大;在屏幕截图中显示的输出下方我可以看到 header 内容):
First half of my output
我的问题
我现在要做的是通过函数的 python 代码访问分配给 logged-in 用户的组,以进一步授权他的请求(例如“用户只能执行指定组 xyz 时的功能,否则将提示他 'not allowed'").
为此,我在我的应用程序注册的令牌配置中添加了“组”声明。
据我所知,使用 ClaimsPrinciple object (source).
可以很容易地通过 .NET 中编码的函数访问用户组。我如何才能通过 python 代码访问用户分配的组?
这可能吗?
我是不是理解错了?
跟进:
我现在不明白的一件事是,当我第一次通过浏览器访问该功能时(触发登录),我可以在 browser-debuggger 的 callback-http-request 中看到一个 id_token
):
Browser debugger: id_token in callback-request
当我使用 jwt.io 解密该令牌时,我能够看到分配的用户组的一些 ID,这似乎正是我想通过 python 代码访问的内容。
Re-loading 页面(我想请求然后使用已经过身份验证的浏览器 session)使回调消失。
header X-MS-CLIENT-PRINCIPAL
包含与 id_token 相同的声明。所以如果我们想得到group claim,我们可以base64解码header.
例如
我的代码
import logging
import azure.functions as func
import base64
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
aadAccessToken = req.headers.get('X-MS-TOKEN-AAD-ACCESS-TOKEN')
principalID = req.headers.get('X-MS-CLIENT-PRINCIPAL-ID')
principalName = req.headers.get('X-MS-CLIENT-PRINCIPAL-NAME')
idProviderId = req.headers.get('X-MS-CLIENT-PRINCIPAL-IDP')
aadRefreshToken = req.headers.get('X-MS-TOKEN-AAD-REFRESH-TOKEN')
clientPrincipal = req.headers.get('X-MS-CLIENT-PRINCIPAL')
clientPrincipal= base64.b64decode(clientPrincipal)
result = "\n"
myDict = sorted(dict(req.headers))
for key in myDict:
result += f"{key} = {dict(req.headers)[key]}\n"
return func.HttpResponse(
f"Hello, {name}. How are you ? Doing well ?"\
f"\n\nHere is some data concerning your Client principal:"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-ID: {principalID}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-NAME: {principalName}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-IDP: {idProviderId}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL: {clientPrincipal}"\
f"\n\nHere is some data concerning your AAD-token:"\
f"\nThis is your X-MS-TOKEN-AAD-ID-TOKEN: {aadIdToken}"\
f"\nThis is your X-MS-TOKEN-AAD-ACCESS-TOKEN: {aadAccessToken}"\
f"\nThis is your X-MS-TOKEN-AAD-REFRESH-TOKEN: {aadRefreshToken}"\
f"\n\n\nresult: {result}"\
)
else:
return func.HttpResponse(
"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
status_code=200
)