Я пытался реализовать единый вход (SSO). У меня есть разные модули приложений внешнего интерфейса, которые работают в разных доменах, и все они используют один сервер API.
- Сервер единого входа https://sso.app.com
- Сервер API https://api.app.com
- Внешний модуль 1 https://module-1.app.com
- Интерфейсный модуль 2 https://module-2.app.com
Поток аутентификации
Поток аутентификации — это проверка модуля FrontEnd на наличие токена в локальном хранилище. Если он не находит токен, он перенаправляет пользователя на конечную точку сервера API, скажем, https://api.app.com/oauth/connect. Сервер API имеет clientId и секреты для сервера единого входа. Сервер API устанавливает URL-адрес модуля внешнего интерфейса в файле cookie (чтобы я мог перенаправить пользователя обратно на модуль внешнего интерфейса инициатора), а затем перенаправить запрос на сервер единого входа, где пользователю предоставляется экран входа в систему. Пользователь вводит туда учетные данные, сервер SSO проверяет учетные данные, создает сеанс. После проверки учетных данных сервер единого входа вызывает конечную точку сервера API с профилем пользователя и токеном доступа. Сервер API получает профиль в сеансе и запрашивает и подписывает свой собственный токен и отправляет его в модуль внешнего интерфейса через параметры запроса. На фронтенде (React APP) для этого есть маршрут. В этом внешнем маршруте я извлекаю токен из queryParams и устанавливаю в локальном хранилище. Пользователь находится в приложении. Точно так же, когда пользователь загружает FrontendModule-2, происходит тот же поток, но на этот раз, потому что сеанс создается сервером SSO при запуске потока FrontendModule-1. он никогда не запрашивает учетные данные для входа и не регистрирует пользователя в системе.
Неудачный сценарий:
Сценарий таков: предположим, есть пользователь JHON, который еще не вошел в систему и не имеет сеанса. Джон наткнулся на URL-адрес Frontend Module 1 в браузере. Модуль внешнего интерфейса проверяет localStorage на наличие токена, он его там не находит, затем модуль внешнего интерфейса перенаправляет пользователя на маршрут сервера API. Сервер API имеет clientSecret и clientId, которые перенаправляют запрос на сервер SSO. Там пользователю будет представлен экран входа в систему.
Джон видит экран входа в систему и оставляет его как есть. Теперь Джон открывает другую вкладку в том же браузере и вводит URL-адрес внешнего модуля 2. Происходит тот же процесс, что и выше, и Джон попадает на экран входа в систему. Джон оставил этот экран как есть и возвращается к первой вкладке, где у него загружен экран сеанса Frontend Module 1. Он вводит кредиты и нажимает кнопку входа. Это дает мне ошибку, что состояние сеанса было изменено. Эта ошибка на самом деле имеет смысл, потому что сеанс является общим.
Ожидание
Как мне добиться этого без ошибки. Я хочу перенаправить пользователя к тому же интерфейсному модулю, который инициировал запрос.
Инструменты, которые я использую
Пример реализации (сервер API)
require('dotenv').config();
var express = require('express')
, session = require('express-session')
, morgan = require('morgan')
var Grant = require('grant-express')
, port = process.env.PORT || 3001
, oauthConsumer= process.env.OAUTH_CONSUMER || `http://localhost`
, oauthProvider = process.env.OAUTH_PROVIDER_URL || 'http://localhost'
, grant = new Grant({
defaults: {
protocol: 'https',
host: oauthConsumer,
transport: 'session',
state: true
},
myOAuth: {
key: process.env.CLIENT_ID || 'test',
secret: process.env.CLIENT_SECRET || 'secret',
redirect_uri: `${oauthConsumer}/connect/myOAuth/callback`,
authorize_url: `${oauthProvider}/oauth/authorize`,
access_url: `${oauthProvider}/oauth/token`,
oauth: 2,
scope: ['openid', 'profile'],
callback: '/done',
scope_delimiter: ' ',
dynamic: ['uiState'],
custom_params: { deviceId: 'abcd', appId: 'com.pud' }
}
})
var app = express()
app.use(morgan('dev'))
// REQUIRED: (any session store - see ./examples/express-session)
app.use(session({secret: 'grant'}))
// Setting the FrontEndModule URL in the Dynamic key of Grant.
app.use((req, res, next) => {
req.locals.grant = {
dynamic: {
uiState: req.query.uiState
}
}
next();
})
// mount grant
app.use(grant)
app.get('/done', (req, res) => {
if (req.session.grant.response.error) {
res.status(500).json(req.session.grant.response.error);
} else {
res.json(req.session.grant);
}
})
app.listen(port, () => {
console.log(`READY port ${port}`)
})
uiState
. Не переопределяйте uiState, а вместо этого добавляйте состояния в массив. И при отправке ответа обратно пользователю проверьте, из какого Frontend-модуля был инициирован запрос, и отправьте соответствующий ответ. Не будет работать? - person Omair Nabiel   schedule 19.08.2020uiState
как["http://app1.com"]
, а затем я делаю другой запрос, я не перезаписываю его, а делаю push в массив, как это["http://app1.com", "http://app2.com"]
, тогда это все еще изменение что приведет к ошибке несоответствия. Пожалуйста, поправьте меня, если я ошибаюсь.? - person Taimoor Ali   schedule 24.08.2020