Search code examples
azureazure-active-directoryoffice-jsoutlook-addinoffice-addins

Unable to get sso token in outlook add-in app


I would get sso token from the backend as described here

In my front I get access token that I pass to back end

OfficeRuntime.auth
  .getAccessToken({ allowConsentPrompt: true, allowSignInPrompt: true, forMSGraphAccess: true })
  .then((token) => {
    console.log(token);
    axios
      .get(process.env.REACT_APP_FUNC_ENDPOINT + URLS.GET_GRAPH_DATA, {
        headers: { Authorization: "Bearer " + token },
      })
      .then((data) => {
        console.log(data);
        event.completed();
      });
  });

In back end side I exchange access token with sso token

export async function getAccessToken(authorization: string): Promise<any> {
  if (!authorization) {
    let error = new Error("No Authorization header was found.");
    return Promise.reject(error);
  } else {
    const scopeName: string = process.env.SCOPE || "User.Read";
    const [, /* schema */ assertion] = authorization.split(" ");

    const tokenScopes = (jwt.decode(assertion) as jwt.JwtPayload).scp.split(
      " "
    );
    const accessAsUserScope = tokenScopes.find(
      (scope) => scope === "access_as_user"
    );
    if (!accessAsUserScope) {
      throw new Error("Missing access_as_user");
    }

    const formParams = {
      client_id: process.env.CLIENT_ID,
      client_secret: process.env.CLIENT_SECRET,
      grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
      assertion: assertion,
      requested_token_use: "on_behalf_of",
      scope: [scopeName].join(" "),
    };

    const stsDomain: string = "https://login.microsoftonline.com";
    const tenant: string = "common";
    const tokenURLSegment: string = "oauth2/v2.0/token";
    const encodedForm = form(formParams);
    let tokenResponse;
    try {
      tokenResponse = await axios.post(
        `${stsDomain}/${tenant}/${tokenURLSegment}`,
        encodedForm,
        {
          headers: {
            Accept: "application/json",
            "Content-Type": "application/x-www-form-urlencoded",
          },
        }
      );
    } catch (error) {
      console.log(error);
    }

    return tokenResponse.data;
  }
}

But I get an error

'AADSTS65001: The user or administrator has not consented to use the application with ID '64f01276-0XXX' named 'ssoaddins'. Send an interactive authorization request for this user and resource.\r\nTrace ID: 9a6a62f0-e698-48f3-ac77-ca18c0bbbd00\r\nCorrelation ID: 58843565-b171-4165-ab24-face78a43dfb\r\nTimestamp: 2023-03-15 01:23:42Z'

In the front end side I'm specifiying allowConsentPrompt: true but user does not get the consent screen. In which cases that happens ?

In Azure I defined the permission which are a deleguated ones, so no need for admin to approve.

enter image description here

I don't know why I get this error ?

Update after adapting my code I get a graph token that does not look as a valid token :

enter image description here


Solution

  • I tried to reproduce the same in my environment via Postman and got below results:

    I registered one Azure AD application and added API permissions same as you like below:

    enter image description here

    Now I set Application ID URI and exposed an API named access_as_user like below:

    enter image description here

    When I tried to get access token using on-behalf-of flow with below parameters via Postman, I got same error as you like this:

    POST https://login.microsoftonline.com/common/oauth2/v2.0/token
    
    client_id: <appID>
    client_secret: <secret>
    scope: User.Read access_as_user
    grant_type: urn:ietf:params:oauth:grant-type:jwt-bearer
    assertion: assertion
    requested_token_use: on_behalf_of
    

    Response: enter image description here

    To resolve the error, try adding access_as_user in API permissions of application like this:

    You can find that permission in My APIs as below:

    enter image description here

    Now, add access_as_user in API permissions of application like this:

    enter image description here

    Make sure to grant admin consent for above added permissions as below:

    enter image description here

    If you generated assertion with access_as_user scope, then pass only Graph related permissions to get access token via on behalf of flow.

    When I passed only User.Read permission in scope, I got access token successfully with on-behalf-of flow as below:

    POST https://login.microsoftonline.com/common/oauth2/v2.0/token
    
    client_id: <appID>
    client_secret: <secret>
    scope: User.Read
    grant_type: urn:ietf:params:oauth:grant-type:jwt-bearer
    assertion: assertion
    requested_token_use: on_behalf_of
    

    Response:

    enter image description here

    In your case, make sure to grant admin consent to all added permissions and pass only graph permissions like User.Read in scopes without joining access_as_user while fetching token via on-behalf-of flow.