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

无需Node.js,JS客户端调用login.microsoftonline.com获取Bearer Token报错求助

问题根源与解决方案

为什么浏览器端调用会返回400?

client_credentials(客户端凭据)模式是服务间认证专用的,Azure AD明确禁止在浏览器端(纯前端JS)使用这个模式:

  • 前端代码会直接暴露client_secret,这属于严重的安全漏洞,任何人都能扒下来盗用你的应用权限。
  • Azure AD的token端点默认拒绝浏览器发起的跨域预检请求(OPTIONS),因为这种模式设计时就没考虑前端场景,所以直接返回400。

Postman和浏览器调用的核心差异

  • Postman是桌面/后端工具,不属于浏览器的跨域安全模型,也不会把client_secret暴露给用户,符合Azure AD对client_credentials模式的使用场景要求。
  • 浏览器端受同源策略限制,发起跨域请求时会先发送OPTIONS预检请求,而Azure AD的token端点不支持前端应用的预检请求,同时前端暴露client_secret的行为也会被安全机制拦截。

你能用到的替代方案(无需Node.js)

放弃client_credentials模式,改用授权码模式+PKCE,这是微软推荐的纯前端应用认证方案,不需要client_secret,也不需要后端服务器:

步骤1:生成PKCE所需的验证参数

// 生成随机的code_verifier
function generateCodeVerifier() {
  let array = new Uint32Array(56/2);
  window.crypto.getRandomValues(array);
  return Array.from(array, dec => ('0' + dec.toString(16)).substr(-2)).join('');
}

// 基于code_verifier生成code_challenge
async function generateCodeChallenge(codeVerifier) {
  const encoder = new TextEncoder();
  const data = encoder.encode(codeVerifier);
  const digest = await window.crypto.subtle.digest('SHA-256', data);
  return btoa(String.fromCharCode(...new Uint8Array(digest)))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/, '');
}

步骤2:跳转到Azure AD授权页面获取授权码

const clientId = "你的client_id";
const tenantId = "你的tenant_id";
const redirectUri = "你的前端回调地址(需在Azure AD应用里配置)";
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);

// 把codeVerifier存在localStorage,后续请求token要用
localStorage.setItem('code_verifier', codeVerifier);

// 构造授权URL并跳转
const authUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize?` + new URLSearchParams({
  client_id: clientId,
  response_type: 'code',
  redirect_uri: redirectUri,
  response_mode: 'query',
  scope: '你要访问的资源scope(比如https://graph.microsoft.com/.default)',
  code_challenge: codeChallenge,
  code_challenge_method: 'S256'
});

window.location.href = authUrl;

步骤3:在回调页面用授权码请求Token

// 从URL中获取授权码
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const codeVerifier = localStorage.getItem('code_verifier');

if (code && codeVerifier) {
  const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
  const formData = new URLSearchParams({
    client_id: clientId,
    grant_type: 'authorization_code',
    code: code,
    redirect_uri: redirectUri,
    code_verifier: codeVerifier,
    scope: '你要访问的资源scope'
  });

  fetch(tokenUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: formData
  })
  .then(res => res.json())
  .then(data => {
    console.log('获取到的Token:', data.access_token);
    // 这里可以处理token,比如存在localStorage或sessionStorage
  })
  .catch(err => console.error('请求Token失败:', err));
}

额外注意事项

  1. 必须在Azure AD应用注册里配置正确的redirect_uri,否则会被拒绝。
  2. 确保你请求的scope是Azure AD应用已经授权过的权限。
  3. 不要在前端代码里存放任何敏感信息(比如client_secret),这是前端应用的基本安全原则。

内容的提问来源于stack exchange,提问作者Pascal Stephan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 21:30:48