Auth0 аутентификация одностраничного приложения в другом домене, чем API

Я пытаюсь добавить аутентификацию Auth0 в мое одностраничное приложение. Мое приложение работает в домене, например, app.mycompany.com, тогда как API, используемый этим приложением, работает в другом домене, например api.mycompany.com.

Я знаю об этой теме:

Решение / архитектура единого входа (SSO) для одностраничного приложения (SPA)

и статьи auth0 и репозитории github, связанные здесь. Но у меня есть ощущение, что мой сценарий немного проще, так как я не обязательно хочу иметь единый вход между несколькими разными одностраничными приложениями. Для начала я просто хочу разделить API и приложение.

Вот что я уже пробовал:

Я уже начал со статьи React Login With Auth0 и скачал стартовый проект. Я, конечно, могу войти без проблем, и это оставит меня с id_token в моем localStorage, содержащем JWS, выпущенный Auth0.

Я также могу войти непосредственно на api.mycompany.com (мое приложение FeathersJS API) и увидеть, что в процессе перенаправления OAuth токен id_token волшебным образом транслируется в токен перьев-jwt, выпущенный моим приложением Feathers, содержащим внутренний идентификатор пользовательский объект, соответствующий auth0-ID. Я также реализовал логику, используемую для сопоставления Auth0-ID с моим внутренним ID. Кроме того, все мои хуки Feathers, такие как проверка токена и популяция пользователя, работают.

Чего я не могу понять, так это как изменить реагирующее приложение, работающее под app.mycompany.com, с токеном Auth0 в localStorage, чтобы этот токен был преобразован в токен перо-jwt с помощью api.mycompany.com в такой Таким образом, все последующие вызовы API автоматически включают токен перьев-jwt, поэтому API может проверять пользователя и возвращать правильные данные.

Будем очень благодарны за любые предложения о том, как поступить.

Несколько дополнительных деталей:

  • API построен на node.js и featherjs (который в основном является расширением Express)
  • Одностраничное приложение построено на ReactJS и обслуживается простым сервером Express, но оно может обслуживаться любым сервером, который может обслуживать статические файлы через http. Приложение с одной страницей делает http-запросы к API для чтения данных и выполнения операций.
  • API имеет следующие строки кода, обеспечивающие аутентификацию:

    const authentication = require('feathers-authentication');
    const Auth0Strategy = require('passport-auth0').Strategy;
    app.configure(authentication({
        local:false,
        token: {
          secret: 'mysecret',
          payload: ['email', 'auth0Nickname'],
          issuer: 'mycompany'
        },
        idField: 'id',
        shouldSetupSuccessRoute: false,
        auth0: {
          strategy: Auth0Strategy,
          domain: 'mycompany.eu.auth0.com',
          'clientID': 'xxx',
          'clientSecret': 'yyy'
        }
    }));
    

2 ответа

Решение

Если вы этого не сделали, вам следует следовать этой статье ( React Login with Auth0), чтобы реализовать аутентификацию в вашем приложении React. Если вы уже пытались выполнить его, обновите свой вопрос, указав конкретные проблемы, с которыми вы столкнулись.

Несмотря на то, что в настоящее время вам не нужен единый вход, реальная реализация аутентификации в вашем приложении не сильно изменится. При использовании Auth0 включение единого входа во всех приложениях в основном включает переключатели конфигурации.

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

Сценарии архитектуры Auth0: SPA + API


Обновить:

Полный сценарий, который я связал, также охватывает наиболее полные сценарии, когда к API-интерфейсу обращается множество клиентских приложений, которые могут даже разрабатываться сторонними разработчиками, которые не владеют защищенным API-интерфейсом, но хотят получить доступ к данным, стоящим за ним.

Это достигается за счет использования последних функций, которые в настоящее время доступны только в регионе США и которые на очень высоком уровне можно охарактеризовать как сервер авторизации OAuth 2.0, предоставляемый в качестве службы.

Ваш конкретный сценарий проще, и API, и клиентское приложение находятся под управлением одной и той же сущности, поэтому у вас есть другой вариант.

Вариант 1. Использование авторизации API только для американского региона Auth0 (на данный момент)

В этой ситуации ваше клиентское приложение во время аутентификации получит id_token который будет использоваться для того, чтобы узнать текущего пользователя, прошедшего проверку подлинности, а также получит access_token это можно использовать для вызова API от имени аутентифицированного пользователя.

Это делает четкое разделение между клиентским приложением и API; id_token для использования клиентских приложений и access_token для использования API.

Преимущество заключается в том, что авторизация четко отделена от аутентификации, и вы можете очень детально контролировать решения об авторизации, управляя областями, включенными в маркер доступа.

Вариант 2. Аутентификация в клиентском приложении и API таким же образом

Вы можете развернуть свое клиентское приложение и API по отдельности, но все же рассматривать их с концептуальной точки зрения как одно и то же приложение (в Auth0 будет настроен один клиент, представляющий как клиентскую часть, так и API).

Это имеет то преимущество, что вы можете использовать id_token это получается после завершения аутентификации, чтобы узнать, кем был пользователь на стороне клиента, а также как механизм аутентификации каждого запроса API.

Вы должны были бы настроить API перьев для проверки Auth0 id_token в качестве принятого токена для доступа к API. Это означает, что вы не используете какие-либо перья на основе аутентификации в API, то есть вы просто принимаете токены, выданные Auth0, для вашего приложения в качестве способа проверки доступа.

У меня была точно такая же проблема, как и у вас, я хотел аутентифицировать пользователя из одностраничного приложения, вызывая API, расположенный на другом сервере.

Официальный пример auth0 - это классическое веб-приложение Express, которое выполняет аутентификацию и отображает html-страницу, но это не SPA, подключенный к API, размещенному в другом домене.

Давайте разберемся, что происходит, когда пользователь проходит аутентификацию в этом примере:

  • Пользователь делает запрос вызова /auth/auth0 маршрут
  • Пользователь автоматически перенаправляется в процесс аутентификации Auth0 (форма авторизации Auth0 для выбора провайдера, а затем экран входа провайдера)
  • Пользователь перенаправлен на /auth/success маршрут
  • /auth/success Маршрут перенаправляет на статическую HTML-страницу public/success.htmlтакже отправив jwt-token файл cookie, содержащий токен пользователя
  • На стороне клиента, когда public/success.html грузы, перья клиент authenticate() Метод считывает токен из куки и сохраняет его в локальном хранилище.

С этого момента клиент Feathers будет аутентифицировать пользователя, читающего cookie-файл из локального хранилища.

Я попытался адаптировать этот сценарий к архитектуре одностраничного приложения, реализуя следующий процесс:

  • Из SPA вызовите API аутентификации с помощью source параметр строки запроса, содержащий URL-адрес SPA. Например: http://my-api.com/auth/auth0?source=http://my-spa.com
  • Серверная сторона, в /auth/auth0 обработчик маршрута, создайте cookie для хранения этого URL
  • После успешного входа в систему прочитайте source cookie, чтобы перенаправить пользователя обратно в SPA, отправив токен JWT в cookie.

Но последний шаг не сработал, потому что вы не можете установить cookie для данного домена (домена сервера API) и перенаправить пользователя в другой домен! (подробнее об этом здесь, на Stackru)

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

  • на стороне сервера: отправка токена клиенту с использованием хэша URL.
  • на стороне клиента: создайте новую HTML-страницу, которая читает токен из хэша URL

Серверный код:

// Add a middleware to write in a cookie where the user comes from
// This cookie will be used later to redirect the user to the SPA
app.get('/auth/auth0', (req, res, next) => {
  const { origin } = req.query
  if (origin) {
    res.cookie(WEB_CLIENT_COOKIE, origin)
  } else {
    res.clearCookie(WEB_CLIENT_COOKIE)
  }
  next()
})

// Route called after a successful login
// Redirect the user to the single-page application "forwarding" the auth token
app.get('/auth/success', (req, res) => {
  const origin = req.cookies[WEB_CLIENT_COOKIE]
  if (origin) {
    // if there is a cookie that contains the URL source, redirect the user to this URL
    // and send the user's token in the URL hash
    const token = req.cookies['feathers-jwt']
    const redirectUrl = `${origin}/auth0.html#${token}`
    res.redirect(redirectUrl)
  } else {
    // otherwise send the static page on the same domain.
    res.sendFile(path.resolve(process.cwd(), 'public', 'success.html'))
  }
})

Сторона клиента, auth0.html страница в СПА

В SPA я создал новую HTML-страницу, которую я назвал auth0.html это делает 3 вещи:

  • он читает токен из хэша
  • он сохраняет его в локальном хранилище (чтобы имитировать то, что делает клиент Feathers)
  • перенаправляет пользователя на главную страницу SPA index.html

HTML-код:

<html>
<body>
  <script>
  function init() {
    const token = getToken()
    if (!token) {
      console.error('No auth token found in the URL hash!')
    }
    // Save the token in the local storage
    window.localStorage.setItem('feathers-jwt', token)
    // Redirect to the single-page application
    window.location.href = '/'
  }

  // Read the token from the URL hash
  function getToken() {
    const hash = self.location.hash
    const array = /#(.*)/.exec(hash)
    if (!array) return
    return array[1]
  }

  init()
  </script>
</body>
</html>

И теперь в SPA я могу использовать клиент Feathers, считывая токен из локального хранилища при запуске приложения.

Дайте мне знать, если это имеет смысл, спасибо!

Другие вопросы по тегам