Angular MSAL获取的Azure AD令牌无法通过Django Auth ADFS验证求助
问题描述
我正在搭建一个Angular单页应用,使用MSAL Angular库获取Azure AD访问令牌,用于调用Microsoft Graph API获取用户信息,同时认证到基于Django框架(依赖django_auth_adfs和rest_framework库)构建的自定义WEB API。已完成基础环境配置,Angular应用可正常通过Azure AD凭据登录,也能成功获取Graph API和自定义API的访问令牌,但调用自定义API时携带的Bearer令牌始终无法通过Django端验证,返回401错误。
Azure AD配置情况
Django后端应用配置
- 应用类型为Web,重定向URI设置为
http://localhost:8000/oauth2/callback,仅允许组织目录内账户访问,未开启隐式流 - 尝试过配置/不配置客户端密钥,验证结果一致
- 无额外令牌配置项
- 已添加Microsoft Graph权限,无需管理员同意且已为组织授予
- 已暴露API范围
api://xxxxxxxx-32d5-4175-9b05-xxxxxxxxxxxx/access_as_user(启用管理员和用户同意),并将Angular应用添加为授权客户端
Angular前端应用配置
- 应用类型为单页应用,重定向URI设置为
http://localhost:4200/,仅允许组织目录内账户访问 - 无客户端密钥
- 无额外令牌配置项
- 已添加Microsoft Graph的
User.Read权限和自定义API的access_as_user权限,均已获得管理员同意
Django的settings.py配置
INSTALLED_APPS = [ ... "django_auth_adfs", "rest_framework", ... ] MIDDLEWARE = [ ... "django_auth_adfs.middleware.LoginRequiredMiddleware", "corsheaders.middleware.CorsMiddleware", ] AUTHENTICATION_BACKENDS = ( "django_auth_adfs.backend.AdfsAuthCodeBackend", "django_auth_adfs.backend.AdfsAccessTokenBackend", ) AUTH_ADFS = { "TENANT_ID": AZURE_AD_TENANT_ID, "CLIENT_ID": AZURE_CLIENT_ID, "RELYING_PARTY_ID": AZURE_CLIENT_ID, "AUDIENCE": AZURE_CLIENT_ID, # "CLIENT_SECRET": AZURE_CLIENT_SECRET, # Tried with or without it "CLAIM_MAPPING": { "first_name": "given_name", "last_name": "family_name", "email": "upn", }, "GROUPS_CLAIM": "roles", "MIRROR_GROUPS": True, "USERNAME_CLAIM": "upn", "LOGIN_EXEMPT_URLS": [ "^api/v0/", # Prevent API from triggering a login redirect ], } REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": [ "django_auth_adfs.rest_framework.AdfsAccessTokenAuthentication", "rest_framework.authentication.SessionAuthentication", ], "DEFAULT_RENDERER_CLASSES": [ "rest_framework.renderers.JSONRenderer", ], "DEFAULT_PERMISSION_CLASSES": [ # Restrict API access for authenticated user by default "rest_framework.permissions.IsAuthenticated", ], }
Angular的app.module.ts配置
export function MSALInstanceFactory(): IPublicClientApplication { return new PublicClientApplication({ auth: { clientId: environment.azure.client_app_id, authority: environment.azure.authority, redirectUri: environment.azure.redirectUri, postLogoutRedirectUri: environment.azure.redirectUri, navigateToLoginRequestUrl: true }, cache: { cacheLocation: BrowserCacheLocation.LocalStorage, storeAuthStateInCookie: isIE, // set to true for IE 11 }, system: { loggerOptions: { loggerCallback: () => { }, logLevel: LogLevel.Info, piiLoggingEnabled: false } } }); } export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration { const protectedResourceMap = new Map<string, Array<string> | null>(); protectedResourceMap.set('https://graph.microsoft.com/v1.0/me', ['user.read']); protectedResourceMap.set('http://localhost:8000/api/v0/', ['api://xxxxxxxx-32d5-4175-9b05-xxxxxxxxxxxx/access_as_user']); return { interactionType: InteractionType.Redirect, protectedResourceMap, }; } export function MSALGuardConfigFactory(): MsalGuardConfiguration { return { interactionType: InteractionType.Redirect, authRequest: { scopes: [ 'user.read', 'openid', 'profile', 'api://xxxxxxxx-32d5-4175-9b05-xxxxxxxxxxxx/access_as_user' ] }, loginFailedRoute: "/" }; }
可能的问题及解决步骤
1. 令牌受众(Audience)不匹配
Django配置中AUTH_ADFS的AUDIENCE和RELYING_PARTY_ID设为了后端应用的CLIENT_ID,但Angular获取的自定义API令牌受众应为你暴露的API标识符api://xxxxxxxx-32d5-4175-9b05-xxxxxxxxxxxx,而非客户端ID。修改配置:
"AUDIENCE": "api://xxxxxxxx-32d5-4175-9b05-xxxxxxxxxxxx", "RELYING_PARTY_ID": "api://xxxxxxxx-32d5-4175-9b05-xxxxxxxxxxxx",
2. 开启调试日志定位验证细节
添加Django日志配置,查看django_auth_adfs的令牌验证过程,明确失败原因(如签名无效、令牌过期、声明缺失等):
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django_auth_adfs': { 'handlers': ['console'], 'level': 'DEBUG', 'propagate': True, }, }, }
3. 完善CORS配置
确保CORS允许Angular域名携带Authorization头访问API,在settings.py中添加:
CORS_ALLOWED_ORIGINS = [ "http://localhost:4200", ] CORS_ALLOW_CREDENTIALS = True
同时将corsheaders.middleware.CorsMiddleware移动到django.middleware.common.CommonMiddleware之前,保证CORS头优先设置。
4. 适配v2版本令牌
Angular作为SPA获取的是Azure AD v2端点的令牌,需确保Django配置支持v2端点:
"AUTHORITY": f"https://login.microsoftonline.com/{AZURE_AD_TENANT_ID}/v2.0",
同时在Azure AD后端应用的清单中,将accessTokenAcceptedVersion设置为2,允许应用接受v2版本令牌。
5. 验证令牌权限范围
用jwt.ms解码自定义API的令牌,检查scp声明是否包含access_as_user。若缺失,确认Angular的protectedResourceMap和authRequest中的scopes配置正确,且Azure AD已为Angular应用授予该权限。
内容的提问来源于stack exchange,提问作者aga

