You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular MSAL获取的Azure AD令牌无法通过Django Auth ADFS验证求助

Angular + MSAL调用Django自定义API返回401令牌验证失败问题排查

问题描述

我正在搭建一个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 04:01:06