如何从Microsoft图形获取刷新令牌

时间:2020-04-11 05:27:28

标签: javascript oauth-2.0 outlook microsoft-graph-api msal.js

我正在尝试创建一个Outlook电子邮件自动化工具。为了使它能够执行诸如在给定时间在用户的文件夹中发送电子邮件之类的事情。 Microsoft允许第三方api通过auth2代表用户触发Microsoft的api。我能够检索必要的访问令牌,但是我需要刷新令牌才能代表用户调用代码,而不必强迫他每小时登录一次。

我当前正在使用Microsoft的javascript身份验证库来接收令牌。根据我的阅读,似乎刷新令牌在令牌请求中必须具有脱机作用域。使用下面的代码,我可以使用访问令牌进行响应,但是仍然无法获取访问令牌。

enter image description here

const tokenRequest = {
  scopes: [
    "https://graph.microsoft.com/Mail.ReadWrite",
    "https://graph.microsoft.com/mail.send",
    "https://graph.microsoft.com/offline_access"
  ]
};

const hostname = "http://localhost:5000";

const backendServerAdress = "http://localhost:8050";
var xhr = new XMLHttpRequest();
xhr.open("POST", backendServerAdress, true);
xhr.setRequestHeader("Content-Type", "application/json");

const msalConfig = {
  auth: {
    clientId: "something",
    redirectUri: hostname + "/homepage/index.html"
  }
};

var loginRequest = {
  scopes: ["Mail.ReadWrite", "mail.send", "offline_access"] // optional Array<string>
};

const TOKEN_ID = "token_id";
const msalInstance = new Msal.UserAgentApplication(msalConfig);

msalInstance.handleRedirectCallback((error, response) => {
  console.log("redirect callback done");
});

async function redirectToDashboard() {
  console.log("redirect to dashboard");
  // var response = await requestTokenSilent();
  var response;
  if (!response || !response.status == 200) {
    response = await requestTokenPopup();
  }

  if (response && response.status == 200) {
    xhr.send(
      JSON.stringify({
        firstname: "something",
        lastname: "something",
        accessToken: "something"
      })
    );
    location.href = hostname;
  } else {
    console.log("Unable to acquire token");
  }
}

function redirectLogin() {
  console.log("redirect called");
  if (!msalInstance.getAccount()) {
    return msalInstance
      .loginRedirect(loginRequest)
      .then(response => {
        console.log(response);
        return response;
      })
      .catch(err => {
        console.log("Authentication error: ", err);
      });
  }

  if (msalInstance.getAccount()) {
    redirectToDashboard();
  }
}

async function requestTokenSilent() {
  console.log("requestTokenSilent");
  if (msalInstance.getAccount()) {
    return msalInstance
      .acquireTokenSilent(tokenRequest)
      .then(response => {
        localStorage.setItem(TOKEN_ID, response.accessToken);
        console.log("response reached: ", response);
        resolve(response);
      })
      .catch(err => {
        if (err.name === "InteractionRequiredAuthError") {
          alert("Authentication failed try again");
        }
      });
  }
}

async function requestTokenPopup() {
  console.log("requestTokenPopup");
  if (msalInstance.getAccount()) {
    return msalInstance
      .acquireTokenPopup(tokenRequest)
      .then(response => {
        localStorage.setItem(TOKEN_ID, response.accessToken);
        return response;
      })
      .catch(err => {
        console.log(err);
        if (err.name === "InteractionRequiredAuthError") {
          alert("Authentication failed try again");
        }
      });
  }
}

3 个答案:

答案 0 :(得分:1)

我正在使用val版本的msal。 V1版本不再支持刷新令牌。有人告诉我msal v2支持刷新令牌,但它目前处于beta版本。

答案 1 :(得分:1)

MSAL.js执行implicit flow来获取访问令牌。该流程根本不会返回刷新令牌,因为刷新令牌在隐式流程中没有用途。刷新是通过隐藏请求完成的。从上面的链接:

隐式授予不提供刷新令牌。 id_tokens和access_tokens都将在短时间后到期,因此您的应用程序必须准备定期刷新这些令牌。要刷新任一类型的令牌,您可以从上执行以下相同的隐藏iframe请求:使用hint = none参数来控制身份平台的行为。如果要接收新的id_token,请确保在response_type和scope = openid中使用id_token以及一个nonce参数。

如果您在调用requestTokenSilent时当前访问令牌已过期,MSAL.js将自动为您执行此操作。

您想要一个刷新令牌,因为您的真正目标是让您的后端服务器进程访问Graph。隐式对此不起作用。相反,您需要使用on-behalf-of flow。您可以在此处阅读所有详细信息,但高级摘要是:

  • 您的JavaScript前端使用MSAL.js通过隐式流为后端Web API获取令牌。它将在后端调用中的Authorization标头中发送此令牌。
  • 您的后端使用代表流将令牌交换为访问令牌,并为Graph刷新令牌。

答案 2 :(得分:0)

首先,我不擅长JavaScript,但是在我看来,您没有拿下refresh_token。 Refresh_tokens是长期的,但是当请求一个新的时,您必须在收到一个新的access_token时更新refresh_token,因为授权服务器可以发布一个新的。

当我为此苦苦挣扎时,在本文中找到了很多帮助。 Microsoft docs v2-oauth2-auth-code-flow