简体   繁体   English

Azure AD B2C 未获取访问令牌

[英]Azure AD B2C Not getting Access Token

I have a Single Page App (SPA) written in ReactJS. I am attempting to query the Graph API. I am using the msal.js library to handle my authentication.我有一个用 ReactJS 编写的单页应用程序 (SPA)。我正在尝试查询图形 API。我正在使用 msal.js 库来处理我的身份验证。 I am using Azure AD B2C to manage my accounts.我正在使用 Azure AD B2C 来管理我的帐户。 What I have so far:到目前为止我所拥有的:

I am able to login to Google and get my profile information back to my SPA.我能够登录到 Google 并将我的个人资料信息返回到我的 SPA。 However, when an accessToken is requested, the result is empty.但是,当请求 accessToken 时,结果为空。 I am positive I'm doing something wrong and I'm guessing it's around my app registration and scopes.我很肯定我做错了什么,我猜这与我的应用程序注册和范围有关。 When I registered my app, it had a default scope called "user_impersonation".当我注册我的应用程序时,它有一个默认的 scope,称为“user_impersonation”。 As I type this, I realize I registered my client (SPA) and not the API. Do I need to register both?当我输入这个时,我意识到我注册了我的客户 (SPA) 而不是 API。我需要同时注册吗? I'm not sure how I would register Graph.我不确定如何注册 Graph。

My code:我的代码:

App.js应用程序.js

import AuthService from '../services/auth.service';
import GraphService from '../services/graph.service';

class App extends Component {
  constructor() {
  super();
  this.authService = new AuthService();
  this.graphService = new GraphService();
  this.state = {
    user: null,
    userInfo: null,
    apiCallFailed: false,
    loginFailed: false
  };
}
  componentWillMount() {}

  callAPI = () => {
    this.setState({
    apiCallFailed: false
  });
  this.authService.getToken().then(
    token => {
      this.graphService.getUserInfo(token).then(
        data => {
          this.setState({
            userInfo: data
          });
        },
        error => {
          console.error(error);
          this.setState({
            apiCallFailed: true
          });
        }
      );
    },
    error => {
      console.error(error);
      this.setState({
        apiCallFailed: true
      });
    }
  );
};

logout = () => {
  this.authService.logout();
};

login = () => {
  this.setState({
    loginFailed: false
  });
  this.authService.login().then(
    user => {
      if (user) {
        this.setState({
          user: user
        });
      } else {
        this.setState({
          loginFailed: true
        });
      }
    },
    () => {
      this.setState({
       loginFailed: true
      });
    }
  );
};

render() {
  let templates = [];
  if (this.state.user) {
    templates.push(
      <div key="loggedIn">
        <button onClick={this.callAPI} type="button">
          Call Graph's /me API
        </button>
        <button onClick={this.logout} type="button">
          Logout
        </button>
        <h3>Hello {this.state.user.name}</h3>
      </div>
    );
  } else {
    templates.push(
      <div key="loggedIn">
        <button onClick={this.login} type="button">
          Login with Google
        </button>
      </div>
    );
  }
  if (this.state.userInfo) {
    templates.push(
      <pre key="userInfo">{JSON.stringify(this.state.userInfo, null, 4)}</pre>
    );
  }
  if (this.state.loginFailed) {
    templates.push(<strong key="loginFailed">Login unsuccessful</strong>);
  }
  if (this.state.apiCallFailed) {
    templates.push(
      <strong key="apiCallFailed">Graph API call unsuccessful</strong>
    );
  }
  return (
    <div className="App">
      <Header />
      <Main />
        {templates}
    </div>
  );
 }
}

export default App

auth.service.js: auth.service.js:

import * as Msal from 'msal';

export default class AuthService {
constructor() {
// let PROD_REDIRECT_URI = 'https://sunilbandla.github.io/react-msal-sample/';
// let redirectUri = window.location.origin;
// let redirectUri = 'http://localhost:3000/auth/openid/return'
// if (window.location.hostname !== '127.0.0.1') {
//   redirectUri = PROD_REDIRECT_URI;
// }
this.applicationConfig = {
  clientID: 'my_client_id',
  authority: "https://login.microsoftonline.com/tfp/my_app_name.onmicrosoft.com/b2c_1_google-sisu",
  b2cScopes: ['https://my_app_name.onmicrosoft.com/my_api_name/user_impersonation email openid profile']
  // b2cScopes: ['graph.microsoft.com user.read']
};
this.app = new Msal.UserAgentApplication(this.applicationConfig.clientID, this.applicationConfig.authority, function (errorDesc, token, error, tokenType) {});
// this.logger = new Msal.logger(loggerCallback, { level: Msal.LogLevel.Verbose, correlationId:'12345' });
}


login = () => {
    return this.app.loginPopup(this.applicationConfig.b2cScopes).then(
    idToken => {
    const user = this.app.getUser();
    if (user) {
      return user;
    } else {
      return null;
    }
  },
   () => {
      return null;
    }
  );
};


logout = () => {
  this.app.logout();
};


getToken = () => {
  return this.app.acquireTokenSilent(this.applicationConfig.b2cScopes).then(
    accessToken => {
      return accessToken;
    },
    error => {
      return this.app
        .acquireTokenPopup(this.applicationConfig.b2cScopes)
        .then(
          accessToken => {
            return accessToken;
          },
          err => {
            console.error(err);
          }
        );
      }
    );
  };
}

graph.service.js图.service.js

export default class GraphService {
constructor() {
  // this.graphUrl = 'https://graph.microsoft.com/v1.0';
  this.graphUrl = 'http://httpbin.org/headers';
}

getUserInfo = token => {
  const headers = new Headers({ Authorization: `Bearer ${token}` });
  const options = {
    headers
  };
  return fetch(`${this.graphUrl}`, options)
    .then(response => response.json())
    .catch(response => {
      throw new Error(response.text());
    });
  };
}

I was getting an access denied error when trying to query the Graph API so I replaced graph.microsoft.com/1.0 with httpbin so I could actually see what was being passed in. This is where I saw that the token was null. This is the exact same code I pulled from Microsoft's example project, which works when I use their code.我在尝试查询 Graph API 时收到访问被拒绝错误,所以我用 httpbin 替换了 graph.microsoft.com/1.0 这样我就可以真正看到传入的内容。这是我看到令牌是 null 的地方。这是我从微软的示例项目中提取的完全相同的代码,当我使用他们的代码时它可以工作。 What they don't show is how AAD B2C and app registrations are configured which is where I believe my problem is.他们没有显示的是 AAD B2C 和应用程序注册的配置方式,我认为这是我的问题所在。

My problem was I needed to register 2 applications: 1 for my client, 1 for the API. Then, in the Azure AD B2C portal, I had to grant the client access to the API. Once I did that, an Access Token was given.我的问题是我需要注册 2 个应用程序:1 个用于我的客户,1 个用于 API。然后,在 Azure AD B2C 门户中,我必须授予客户访问 API 的权限。一旦我这样做,就会获得一个访问令牌.

Sorry, it may be a bit confusing for users, but there are several different services here.抱歉,这可能会让用户感到困惑,但这里有几种不同的服务。

There is Azure AD Graph API - it is same for B2CAzure AD 图 API - B2C 相同

There is Microsoft Graph .Microsoft Graph

Then, within Azure AD B2C itself, you have your Application Registration for the B2C part.然后,在 Azure AD B2C 本身中,您拥有B2C部分的申请注册 Using this application registration you cannot use any of the Graphs.使用此应用程序注册,您不能使用任何图形。

There is also a normal Application Integration in Azure Active Directory which is valid for the Graphs. 在 Azure Active Directory 中还有一个正常的应用程序集成,它对图形有效。 However this type of application integrations (also as described here ) cannot be used for B2C flows.但是,此类应用程序集成(也如此所述)不能用于B2C流程。

So, to make it clear again:所以,再说一遍:

You cannot use any of the Graph APIs (neither Azure AD Graph API nor Microsoft Graph) from the context of an B2C application.您不能在B2C应用程序的上下文中使用任何 Graph API(Azure AD Graph API 和 Microsoft Graph)。

AND

You cannot use any B2C functionality (like login with google, etc.) from the context of "normal" app registered for use with Graph.您不能在注册用于 Graph 的“普通”应用程序的上下文中使用任何B2C 功能(如使用 google 登录等)。

So, in context of B2C registered application you can only request and get an access_token for an API which is also registered for use with B2C .因此,在B2C 注册应用程序的上下文中,您只能请求并获取 API 的 access_token ,它也已注册用于 B2C

First log into your azure portal using link https://portal.azure.com and click the filter button and change the directory into created azure b2c tenant首先使用链接https://portal.azure.com登录您的 azure 门户,然后单击过滤器按钮并将目录更改为已创建的 azure b2c 租户

在此处输入图像描述

Then click on All Services and search Azure AD B2C Click Applications and then click Add and then fill the form with a unique name as the below figure然后点击所有服务并搜索Azure AD B2C点击应用程序然后点击添加然后填写一个唯一的名称如下图

在此处输入图像描述

Using this URI, you will allow the permission to your application to access certain features in your directory.使用此 URI,您将允许您的应用程序访问目录中的某些功能。 As an example, this could be reading user profile information.例如,这可能是读取用户配置文件信息。 After creating the Application you have to make sufficient permission for that pls follow below steps Click on the Applications label Click on the application you just created.创建应用程序后,您必须获得足够的权限,请按照以下步骤操作 单击应用程序 label 单击您刚刚创建的应用程序。 Click on API access label Click + Add Select the application in step 4 from the Select API drop down.单击 API 访问 label 从 Select API 下拉列表中单击 + 添加步骤 4 中的应用程序 Select。 Select “Access this app on behalf of the signed-in user…” Click OK Then click create button In the left pane of Azure AD B2C you can find the label named User Flows click on that. Select “代表登录用户访问此应用程序...” 单击确定然后单击创建按钮 在 Azure AD B2C 的左侧窗格中,您可以找到名为 label 的用户流,单击它。 Then click on the + New然后点击+新建

User flow用户流程

在此处输入图像描述

Then click on the Sign up and Sign In and then select Recommended under the version and click Create after that give a name for your user flow and tick email sign up also tick the details you want in the return token under User attributes and token claims like the below images然后单击“注册并登录”,然后在版本下单击“select 推荐”,然后单击“创建”,然后为您的用户流程命名并勾选 email 注册并在“用户属性”和“令牌声明”下的返回令牌中勾选您想要的详细信息,例如下面的图片

在此处输入图像描述

在此处输入图像描述

Now you can see the created user flows and even you can run user flow using the Run user flow现在您可以看到创建的用户流,甚至可以使用运行用户流来运行用户流

在此处输入图像描述

Now let's get started by creating a new ReactJS Project现在让我们开始创建一个新的 ReactJS 项目

Go to your directory you wish to create a reactjs application and type the below command to create a react in cmd opened. Go 到你希望创建 reactjs 应用程序的目录,然后键入以下命令以在 cmd 打开创建一个 React。

$ npx create-react-app b2c-react $ npx create-react-app b2c-react

Now navigate to the newly created project directory by typing现在通过键入导航到新创建的项目目录

$ cd b2c-react $ cd b2c-反应

to run the react app运行反应应用程序

$ npm start $ npm 开始

We can go with two ways to achieve the target of get create login into your reactJS application using Azure Active Directory B2C.我们可以通过两种方式 go 使用 Azure Active Directory B2C 实现创建登录到您的 reactJS 应用程序的目标。

Method I方法一

1st - Go to access the b2c user flow created using Library provided by Microsoft that is using msal library. 1st - Go 访问使用 Microsoft 提供的库创建的 b2c 用户流,该库使用 msal 库。 For that create a new file named B2c.js and add the code provided note that you have to configure the msalAppConfig as per your created instance that is,为此,创建一个名为 B2c.js 的新文件并添加提供的代码,请注意,您必须根据创建的实例配置 msalAppConfig,即,

var msalAppConfig = {
auth: {
clientId: '<client Id>',
authority:'https://<TenantSubDomain>.b2clogin.com/<Tenant>/<signInPolicy>',
redirectUri: '<Redirect Url>',
validateAuthority: false,
postLogoutRedirectUri: 'window.location.origin'},
cache: {
cacheLocation: 'sessionStorage',
storeAuthStateInCookie: isIE()
}

Now need to replace the <> items from values in Azure AD B2C Application.现在需要替换 Azure AD B2C 应用程序中值的 <> 项。 To grab the value for the tenant, go back to your Azure AD B2C directory.要为租户获取价值,请将 go 返回到您的 Azure AD B2C 目录。 Under overview, copy the value in “Domain name” field.在概述下,复制“域名”字段中的值。 tenant sub domain is first part of the tenant before before onmicrosoft.com. tenant 子域是 onmicrosoft.com 之前租户的第一部分。

在此处输入图像描述

Now, to grab the clientId, click on the Applications label, and copy the id from the newly created application and replace the value at applicatoinId field.现在,要获取 clientId,请单击 Applications label,然后从新创建的应用程序中复制 id 并替换 applicatoinId 字段中的值。

在此处输入图像描述

Now click on the User flows (polices) label and copy the name of the policy and replace the value at signInPolicy field.现在单击用户流(策略)label 并复制策略名称并替换 signInPolicy 字段中的值。

在此处输入图像描述

The complete B2c.js will look like this完整的 B2c.js 看起来像这样

import React from 'react'; import * as msal from 'msal'

const state = {
    stopLoopingRedirect: false,
    config: {
      scopes: [],
      cacheLocation: null,
    },
    launchApp: null,
    accessToken: null,
    msalObj: null,   }

const LOCAL_STORAGE = 'localStorage' const SESSION_STORAGE = 'sessionStorage' const AUTHORIZATION_KEY = 'Authorization'    var isIE
= function isIE() {
    var ua = window.navigator.userAgent;
    var msie = ua.indexOf("MSIE ") > -1;
    var msie11 = ua.indexOf("Trident/") > -1;
    return msie || msie11;   };

  var B2C_SCOPES = {
    API_ACCESS: {
      scopes: ['https://b2cunicorn.onmicrosoft.com/api/user_impersonation']
    }   };
     var msalAppConfig = {
    auth: {
      clientId: '',
      authority: 'https://b2cunicorn.b2clogin.com/b2cunicorn.onmicrosoft.com/B2C_1_signup',
      redirectUri: 'http://localhost:3000',
      validateAuthority: false,
      postLogoutRedirectUri: 'window.location.origin'
    },
    cache: {
      cacheLocation: 'sessionStorage',
      storeAuthStateInCookie: isIE()
    }   };

  function acquireToken (successCallback) {
    const account = msalApp.getAccount()
  
    if (!account) {
      msalApp.loginRedirect(B2C_SCOPES.API_ACCESS)
    } else {
      msalApp.acquireTokenSilent(B2C_SCOPES.API_ACCESS).then(accessToken => {  
        if (msalAppConfig.cache.cacheLocation === LOCAL_STORAGE) {
            window.localStorage.setItem(AUTHORIZATION_KEY, 'Bearer ' + accessToken)
          } else {
            window.sessionStorage.setItem(AUTHORIZATION_KEY, 'Bearer ' + accessToken)
          }
          
        state.accessToken = accessToken
        if (state.launchApp) {
          state.launchApp()
        }
        if (successCallback) {
          successCallback()
        }
      }, error => {
        if (error) {
          msalApp.acquireTokenRedirect(B2C_SCOPES.API_ACCESS)
        }
      })
    }   }

  let msalApp;

  var authentication = {
    initialize: () => {
        console.log("aaa");
        msalApp = new msal.UserAgentApplication(msalAppConfig)
      },
    run: (launchApp) => {
        state.launchApp = launchApp
        msalApp.handleRedirectCallback(error => {
          if (error) {
            const errorMessage = error.errorMessage ? error.errorMessage : "Unable to acquire access token."
            console.log(errorMessage)
          }
        });
      acquireToken();
    },
    required: (WrappedComponent, renderLoading) => {
        return class extends React.Component {
          constructor (props) {
            super(props)
            this.state = {
              signedIn: false,
              error: null
            }
          }
    
          render () {
            if (this.state.signedIn) {
              return (<WrappedComponent {...this.props} />)
            }
            return typeof renderLoading === 'function' ? renderLoading() : null
          }
        }
      },

    signOut: function signOut() {
      return msalApp.logout();
    },
    getAccessToken: function getAccessToken() {
      return state.accessToken;
    }   };

  export default authentication;

Then edit index.js as follows然后编辑index.js如下

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import authentication from './B2C';

authentication.initialize();

authentication.run(()=>{
  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>,
    document.getElementById('root')
  );
  serviceWorker.unregister();
});

Method II方法二

This is using a third party library developed to sign In to azure active directory b2c.这是使用开发的第三方库登录到 azure 活动目录 b2c。 This library is well written for this task and I recommended to use it if you does not need special configuration as per your requirement.这个库是为这个任务而写的,如果您不需要根据您的要求进行特殊配置,我建议您使用它。 For that please install library using below command.为此,请使用以下命令安装库。

$ npm install react-azure-b2c --save $ npm 安装 react-azure-b2c --save

then configure the below object as per above information gained form Azure active directory.然后根据从 Azure 活动目录中获得的上述信息配置以下 object。

authentication.initialize({
  instance: 'https://<Tenant_Sub_Domain>.b2clogin.com/', 
  tenant: '<YOUR-B2C-TENANT>',
  signInPolicy: '<SIGNIN-POLICY>',
  clientId: '<APPLICATION_ID>',
  cacheLocation: 'sessionStorage',
  scopes: [<SCOPE_ARRAY>],
  redirectUri: 'http://localhost:3000',
  postLogoutRedirectUri: window.location.origin,
});

Then your index.js file should be looked as below那么你的 index.js 文件应该如下所示

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import authentication from 'react-azure-b2c';


authentication.initialize({
  instance: 'https://b2cunicorn.b2clogin.com/', 
  tenant: 'b2cunicorn.onmicrosoft.com',
  signInPolicy: 'B2C_1_signup',
  clientId:'<application id>',
  cacheLocation: 'sessionStorage',
  scopes: ['https://b2cunicorn.onmicrosoft.com/api/user_impersonation'],
  redirectUri: 'http://localhost:3000',
  postLogoutRedirectUri: window.location.origin,
  
});

authentication.run(()=>{
  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>,
    document.getElementById('root')
  );
  serviceWorker.unregister();
});

After Method I or Method II you able make your login by using secured Azure active directory.在方法 I 或方法 II 之后,您可以使用安全的 Azure 活动目录进行登录。 The redirected portal will shown as below.重定向的门户将如下所示。

You will be able to customize the interface as you wish I hope to write about it in future.您将能够按照自己的意愿自定义界面,我希望将来会写到。

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Postman Newman 在 Azure AD B2C 中获取 web Api 的访问令牌 - Postman Newman get access token for web Api in Azure AD B2C Azure AD B2C JWT 令牌签名验证失败 - Azure AD B2C JWT Token Signature Validation Failed Azure B2C 自定义策略 - ID/访问令牌未通过刷新令牌获得最新声明 - Azure B2C Custom Policy - ID/Access tokens are not getting latest claims through Refresh Token 如何从客户端获取登录用户的 access_token? 使用Azure AD B2C混合流 - How to get access_token from client side for signed in user? using Azure AD B2C hybrid flow azure功能与AD B2C整合 - azure functions and AD B2C integration 品牌 Azure 广告 B2C 电子邮件 - Brand Azure AD B2C emails 是否可以使用 Azure Function 应用程序通过额外声明丰富 Azure AD B2C 令牌? - Is enriching an Azure AD B2C Token with extra claims using an Azure Function app possible? Azure Ad b2c:成功登录azure ad b2c后在Claims中获得email - Azure Ad b2c: Get email in Claims after successfully Signin in azure ad b2c 使用 Azure AD B2C on.Net Core 3.1 获取 OBO 流的错误 AADST50013 - Getting error AADST50013 for OBO flow using Azure AD B2C on .Net Core 3.1 Azure AD B2C API 通过Postman和Web app访问 - Azure AD B2C API Access through Postman and Web app
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM