Cognito / Flask / React: как войти в бэкэнд? - PullRequest
0 голосов
/ 14 декабря 2018

Мое веб-приложение использует React во внешнем интерфейсе и Flask во внутреннем интерфейсе.

Я хотел бы добавить AWS Cognito для пользователяуправление.Согласно документации , для этого требуется не более чем завершение оператора export во внешнем интерфейсе, то есть изменение строки

export default App;

в файле App.js (определяющей основное приложениеРеагировать компонент) на

export default withAuthenticator(App, true);

плюс добавление обязательных операторов import.

Это тривиальное изменение дает мне все ориентированные на пользователя функциональные возможности, требуемые любым разумным веб-приложением, используемым в производстве, например

  • регистрация пользователя
  • подтверждение по электронной почте
  • вход / выход из системы
  • забытый пароль
  • и т. Д.стр.

, включая

  • themable UI
  • службы отправки электронной почты
  • федеративные входы (например, Google, Facebook и т. д.)

Проблема: Я не вижу, как интегрировать это с бэкэндом.

Подход: (как мне кажетсядолжно быть)

  1. существующий пользователь входит в веб-приложение, используя элементы пользовательского интерфейса, предоставленные Cognito и отображаемые внешним интерфейсом
  2. , при успешном входе в систему Cognito делает пользовательские данные доступными посредствомcurrentAuthenticatedUser();среди прочего, веб-токен JSON (JWT)
  3. всякий раз, когда веб-интерфейс отправляет запросы бэкэнду, он передает этот JWT в качестве заголовка запроса
  4. , бэкэнд использует этот JWTчтобы определить, правильно ли пользователь вошел в систему, для какого пользователя сделан запрос и имеет ли этот пользователь достаточные разрешения

Я реализовал обычные обработчики запросов Flask /login или /logout иlogin_required декоратор, а также передача JWT и заявка на декодирование работает нормально; однако , я не вижу, где веб-интерфейс должен уведомлять сервер о входе пользователя в систему.

Я пытался сделать это с помощью Cognito Post Authentication Lambda Trigger ,но это имеет ряд существенных недостатков:

Для начала лямбда-функция AWS, запускаемая при входе пользователя в систему, должна иметь доступ к бэкэнду, поэтому она должна быть общедоступной.Из соображений безопасности я бы предпочел, чтобы сервер был виден / доступен только для внешнего интерфейса.Кроме того, во время разработки я запускаю как интерфейс, так и бэкэнд в контейнерах Docker на моем MacBook;этот подход заставляет меня запускать хотя бы бэкэнд в облаке.

Самая большая проблема в том, что он не работает : с точки зрения бэкэнда выполняются запросы от внешнего интерфейса и Cognitoв отдельных сеансах, поэтому даже после того, как пользователь входит в систему с использованием Cognito (через интерфейс) и Cognito уведомляет серверную часть об этом входе в систему (через лямбда-триггер), любые последующие запросы от интерфейса к серверу - с точки зрения серверной части - несвязаны с (аутентифицированным) сеансом между бэкендом и Cognito и поэтому не аутентифицированы.

Ну, кроме огромных недостатков и того факта, что он не работает, подход лямбда-триггеров также чувствует полное перерасход.

Обновление: Я не знаю, почему я не видел этого раньше, но в документах Amplify есть раздел , который точно объясняет, как настроить withAuthenticator,Тем не менее, я не хочу переопределять render(), как в примере, но signIn() (или я так считаю).Я успешно реализовал код для отправки запроса на http://mybackend.mydomain.com/login с соответствующими заголовками для входа в серверную часть, но я не могу понять, где / как вызвать этот код.

Я скопировалкод из документов Amplify, связанных выше, посмотрел исходный код SignIn JSX и версию ("rendered" / "built"?) в моей локальной папке с исходным кодом на node_modules/aws-amplify-react/dist/Auth/SignIn.js и попробовал это:

class MySignIn extends SignIn {
  async signIn() {
    console.log('MySignIn.signIn() start');
    super.signIn()
        .then(data => console.log(data))
        .catch(err => console.log(err));
    console.log('MySignIn.signIn() end');
  }
}

но все, что я получаю, это пустая страница и никаких сообщений в журнале.

Новый вопрос: Как мне реализовать MySignIn, чтобы он выполнял всю SignIn работу, но после успешного входа в систему также выполняет дополнительную работу, которая мне нужна?

СпасибоВы очень за ваше внимание!: -)

1 Ответ

0 голосов
/ 18 декабря 2018

Я проверил код, и функция signIn кажется сложной для расширения, поскольку она ничего не возвращает, поэтому нет смысла использовать super и then, потому что функция разрешения не собирается получать какие-либо данные,Вот почему ваши журналы пусты.Чтобы использовать его, вам нужно по существу переопределить его с нуля.

Что вы можете сделать, это переопределить сам метод changeState, так как в этом случае вы можете просто вызвать родительский метод и затем делать все, что угодновы хотите потом.

Но есть проблема с простым расширением любого из Auth компонентов, так как либо render, либо showComponent функция должна быть переопределена, если вы хотитеподкласс их, потому что родительские - как родитель скрыт сейчас - не будет ничего возвращать.(Вероятно, это то, что они должны прояснить в документах, хотя в их примере функция render явно переопределяется).

Простой способ вызова метода showComponent родительского класса без негоВозврат пустой страницы означает удаление родительского класса из списка скрытых компонентов, которые получает каждый компонент.

Так что вместе с расширением метода changeState будет выглядеть примерно так:

class MySignIn extends SignIn {
  showComponent(theme) {
    const signIn = this.props.hide.indexOf(SignIn)
    this.props.hide.splice(signIn, 1) // make sure SignIn is not hidden
    return super.showComponent(theme) // call showComponent from SignIn
  }
  changeState(state, data) {
    super.changeState(state, data)
    if (state !== 'signedIn') return
    console.log(data)
    // make the request to the backend
  }
}

Теперь вы можете передать MySignIn в точности так, как это показано в документации.

Имейте в виду, что изменение props должно быть анти-паттерном, но я не нашелеще один вариант, который не предполагает полного переопределения метода showComponent (или, что еще хуже, render), поскольку в родительском объекте это именно то, что он ищет.

Вероятно, идея для PR заключается в создании этих методов.(как signIn) более легко расширяемый.

Старый ответ:

Это невозможно реализовать, поскольку свойство onStateChange перезаписывается при компоновкеФактически создается nent.

Альтернативный вариант - передать функцию в onStateChange в качестве опоры для вашего компонента входа и проверить состояние signedIn, например:

function onStateChange(state, data) {
  if (state !== 'signedIn') return
  console.log(data)
  // do the request to the backend here
}

А в ваших настройках withAuthenticator

export default withAuthenticator(App, false, [
  <SignIn onStateChange={ onStateChange } />,
  // the rest
...