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, считывая токен из локального хранилища при запуске приложения.
Дайте мне знать, если это имеет смысл, спасибо!