Azure Идентификационные данные для Azure информации о ресурсах зарегистрированного пользователя? - PullRequest
0 голосов
/ 23 февраля 2020

Что у меня есть

Я использую два разных Node.js проекта, но я хотел бы соединить их.

  1. Сначала используется интерактивный вход в систему (код представлен в браузере), затем выполняется поиск всех моих (зарегистрированных пользователей) арендаторов.
  2. Второй использует реакцию, чтобы нажать на кнопку, чтобы войти в систему и получить информацию из Microsoft Graph.

Azure разрешения API приложения

Приложение Azure для обоих имеет разрешение user_impersonation (для информации об арендаторе / подписке) и user.read (информация профиля).

enter image description here

Код для приложения React

Я использовал это руководство, предоставленное Microsoft . Единственное изменение, которое я сделал, - это добавление user_impersonation.

// config.js
module.exports = {
    appId: '1760c31a-....-baf68c2b3244',
    redirectUri: 'http://localhost:3000',
    scopes: [  'user.read'
    ]
  };

Он использует следующую библиотеку аутентификации и функцию входа в систему:

 // app.js
  import { UserAgentApplication } from 'msal';

class App extends Component {
  constructor(props) {
    super(props);

    console.log(JSON.stringify(props));

    this.userAgentApplication = new UserAgentApplication({
      auth: {
        clientId: config.appId,
        redirectUri: config.redirectUri
      },
      cache: {
        cacheLocation: "localStorage",
        storeAuthStateInCookie: true
      }
    });

    var user = this.userAgentApplication.getAccount();

    this.state = {
      isAuthenticated: (user !== null),
      user: {},
      subscriptions: {},
      error: null
    };

    if (user) {
      // Enhance user object with data from Graph
      this.getUserProfile();
    }
  }

  setErrorMessage(message, debug) {
    this.setState({
      error: { message: message, debug: debug }
    });
  }

  async login() {
    try {
           await this.userAgentApplication.loginPopup(
           {
                scopes: [
                     'user.read',
                     'calendars.read'
                ],
                prompt: "select_account"
            });
           await this.getUserProfile();
    }
    catch (err) {
      var error = {};

      if (typeof (err) === 'string') {
        var errParts = err.split('|');
        error = errParts.length > 1 ?
          { message: errParts[1], debug: errParts[0] } :
          { message: err };
      } else {
        error = {
          message: err.message,
          debug: JSON.stringify(err)
        };
      }

      this.setState({
        isAuthenticated: false,
        user: {},
        error: error
      });
    }
  }

  logout() {
    this.userAgentApplication.logout();
  }
  async getSubscriptions(bearerToken) {

    const requestConfig = {
      url: "tenants",
      method: "GET",
      baseURL: "https://management.azure.com/",
      params: {
        'api-version': '2018-01-01'
      },
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${bearerToken}`
      }
    }
    const subscriptions = await httpRequest(requestConfig);
    console.log(JSON.stringify(subscriptions.data));
    return subscriptions.data;
  }
  async getUserProfile() {
    try {
      // Get the access token silently
      // If the cache contains a non-expired token, this function
      // will just return the cached token. Otherwise, it will
      // make a request to the Azure OAuth endpoint to get a token

      var accessTokenGraphQL = await this.userAgentApplication.acquireTokenSilent({
        scopes: [
          'user.read',
          'calendars.read'
        ]
      });

      // JSON.stringify(accessTokenGraphQL.scopes) returns
      // ["User.Read","Calendars.Read","openid","profile"]

      var accessTokenSubscriptionList = await this.userAgentApplication.acquireTokenSilent({
        scopes: [
          'https://management.azure.com/user_impersonation'
        ]
      });

      if (accessTokenGraphQL) {
        // Get the user's profile from Graph
        console.log(`accessToken = ${JSON.stringify(accessTokenGraphQL)}`)
        var user = await getUserDetails(accessTokenGraphQL);

        if(accessTokenSubscriptionList){
          var subscriptions = await getSubscriptions(accessTokenSubscriptionList)
          console.log(`subscriptions = ${JSON.stringify(subscriptions)}`)
          user.subscriptions = subscriptions;
        }

        this.setState({
          isAuthenticated: true,
          user: {
            displayName: user.displayName,
            email: user.mail || user.userPrincipalName,
            accessToken: accessTokenGraphQL
          },
          error: null
        });
      }
    }
    catch (err) {
      var error = {};
      if (typeof (err) === 'string') {
        var errParts = err.split('|');
        error = errParts.length > 1 ?
          { message: errParts[1], debug: errParts[0] } :
          { message: err };
      } else {
        error = {
          message: err.message,
          debug: JSON.stringify(err)
        };
      }

      this.setState({
        isAuthenticated: false,
        user: {},
        error: error
      });
    }
  }

  render() {
    let error = null;
    if (this.state.error) {
      error = <ErrorMessage message={this.state.error.message} debug={this.state.error.debug} />;
    }

    return (
      <Router>
        <div>
          <NavBar
            isAuthenticated={this.state.isAuthenticated}
            authButtonMethod={this.state.isAuthenticated ? this.logout.bind(this) : this.login.bind(this)}
            user={this.state.user} />
          <Container>
            {error}
            <Route exact path="/"
              render={(props) =>
                <Welcome {...props}
                  isAuthenticated={this.state.isAuthenticated}
                  user={this.state.user}
                  authButtonMethod={this.login.bind(this)} />
              } />
            <Route exact path="/calendar"
              render={(props) =>
                <Calendar {...props}
                  showError={this.setErrorMessage.bind(this)} />
              } />
            <div>{JSON.stringify(this.state.user.accessToken)}</div>
            <hr></hr>
          </Container>
        </div>
      </Router>
    );
  }
}

Я получаю сообщение об ошибке:

`Предоставленное значение для входного параметра 'scope' не является действительным. Область 'https://management.azure.com/user_impersonation профиль openid' не существует.

{"errorCode": "invalid_scope", "errorMessage": "Предоставленное значение для входного параметра" scope ": недопустимо. Область 'https://management.azure.com/user_impersonation профиль openid' не существует. "," name ":" ServerError "}`

1 Ответ

0 голосов
/ 24 февраля 2020

Область действия не верна. Нет области действия под названием user.impersonation. Это должно быть https://management.azure.com/user_impersonation.

Кроме того, вы не можете иметь один токен с несколькими аудиториями. https://management.azure.com для azure ресурсов. user.read (https://graph.microsoft.com/User.Read) - для Azure ресурсов AD.

Вам следует дважды вызвать acquireTokenSilent метод для разных аудиторий.

var accessToken = await this.userAgentApplication.acquireTokenSilent({
        scopes: config.scopes
      });

Обновление:

enter image description here

...