在使用Azure的AD B2C框架时,我在基本的用户管理方面苦苦挣扎。
我已经成功设置了Azure AD B2C资源,注册了面向消费者的Web应用程序(创建了客户端机密,并向User.ReadWrite.All授予了委托和应用程序的权限),创建了自定义属性,并添加了现成的注册和登录用户流程。此外,我已经成功注册了用户并登录了我的Web应用程序。
为此,我遵循了文档(ms-identity-python-webapp-master)中提供的Python示例:
app.py
@app.route("/login")
def login():
session["state"] = str(uuid.uuid4())
# Technically we could use empty list [] as scopes to do just sign in,
# here we choose to also collect end user consent upfront
auth_url = _build_auth_url(scopes=app_config_b2c.SCOPE, state=session["state"])
return render_template("templates/login.html", auth_url=auth_url, version=msal.__version__)
@app.route(app_config_b2c.REDIRECT_PATH) # Its absolute URL must match your app's redirect_uri set in AAD
def authorized():
if request.args.get('state') != session.get("state"):
return redirect(url_for("index")) # No-OP. Goes back to Index page
if "error" in request.args: # Authentication/Authorization failure
return render_template("auth_error.html", result=request.args)
if request.args.get('code'):
cache = _load_cache()
result = _build_msal_app(cache=cache).acquire_token_by_authorization_code(
request.args['code'],
scopes=app_config_b2c.SCOPE, # Misspelled scope would cause an HTTP 400 error here
redirect_uri=url_for("authorized", _external=True))
if "error" in result:
return render_template("auth_error.html", result=result)
session["user"] = result.get("id_token_claims")
_save_cache(cache)
return redirect(url_for("index"))
@app.route("/logout")
def logout():
session.clear() # Wipe out user and its token cache from session
return redirect( # Also logout from your tenant's web session
app_config_b2c.AUTHORITY + "/oauth2/v2.0/logout" +
"?post_logout_redirect_uri=" + url_for("index", _external=True))
@app.route("/graphcall")
def graphcall():
token = _get_token_from_cache(app_config_b2c.SCOPE)
if not token:
return redirect(url_for("login"))
graph_data = requests.get( # Use token to call downstream service
app_config_b2c.ENDPOINT,
headers={'Authorization': 'Bearer ' + token['access_token']},
).json()
return render_template('templates/display.html', result=graph_data)
def _load_cache():
cache = msal.SerializableTokenCache()
if session.get("token_cache"):
cache.deserialize(session["token_cache"])
return cache
def _save_cache(cache):
if cache.has_state_changed:
session["token_cache"] = cache.serialize()
def _build_msal_app(cache=None, authority=None):
return msal.ConfidentialClientApplication(
app_config_b2c.CLIENT_ID, authority=authority or app_config_b2c.AUTHORITY,
client_credential=app_config_b2c.CLIENT_SECRET, token_cache=cache)
def _build_auth_url(authority=None, scopes=None, state=None):
return _build_msal_app(authority=authority).get_authorization_request_url(
scopes or [],
state=state or str(uuid.uuid4()),
redirect_uri=url_for("authorized", _external=True))
def _get_token_from_cache(scope=None):
cache = _load_cache() # This web app maintains one cache per session
cca = _build_msal_app(cache=cache)
accounts = cca.get_accounts()
if accounts: # So all account(s) belong to the current signed-in user
result = cca.acquire_token_silent(scope, account=accounts[0])
_save_cache(cache)
return result
app_config_b2c.py
import os
b2c_tenant = "myapplication"
signupsignin_user_flow = "b2c_1_signupsignin1"
editprofile_user_flow = "b2c_1_profileediting1"
resetpassword_user_flow = "b2c_1_passwordreset1"
authority_template = "https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{user_flow}"
CLIENT_SECRET = "Enter_the_Client_Secret_Here" # Our Quickstart uses this placeholder
# In your production app, we recommend you to use other ways to store your secret,
# such as KeyVault, or environment variable as described in Flask's documentation here
# https://flask.palletsprojects.com/en/1.1.x/config/#configuring-from-environment-variables
# CLIENT_SECRET = os.getenv("CLIENT_SECRET")
# if not CLIENT_SECRET:
# raise ValueError("Need to define CLIENT_SECRET environment variable")
AUTHORITY = authority_template.format(
tenant=b2c_tenant, user_flow=signupsignin_user_flow)
B2C_PROFILE_AUTHORITY = authority_template.format(
tenant=b2c_tenant, user_flow=editprofile_user_flow)
B2C_RESET_PASSWORD_AUTHORITY = authority_template.format(
tenant=b2c_tenant, user_flow=resetpassword_user_flow)
CLIENT_ID = "xxx.xxxxxx"
REDIRECT_PATH = "/getAToken" # It will be used to form an absolute URL
# And that absolute URL must match your app's redirect_uri set in AAD
# This is the resource that you are going to access in your B2C tenant
ENDPOINT = 'https://graph.microsoft.com/v1.0/users'
# These are the scopes that you defined for the web API
SCOPE = ["User.ReadWrite.All"]
SESSION_TYPE = "filesystem" # So token cache will be stored in server-side session
graphcall在此框架内不起作用(也许是b2c问题),我确定这是问题的一部分,但最终我还是希望应用程序使用已登录用户的AD属性(特别是我启用的自定义属性),并在必要时进行修改。例如,说一个自定义属性是“ paid_subscriber”。用户注册时,该属性为空。当用户购买内容时,我想将属性的值设置为相关的值(例如“ true”)。
这可能吗?我是否需要其他用户流程?从理论上和实践上我在这里想念什么?