[英]Discourse SSO with Firebase (Discourse Connect)
我有一个使用 firebase 身份验证的网站,我想使用这些相同的登录详细信息进行讨论,实现此目的的最佳方法是什么?
这是我发现最优雅的工作流程。 只要您在主站点上登录,它就允许通过单击登录,因为 firebase 身份验证将在访问时自动登录。
数据流是这样的:
forum -> site
site -> server
server -> site -> forum
这是用于创建它的代码:
forum
- 话语连接 Urlhttps://MY-SITE.com/forum-sign-in
site
页面访问处理程序当人们访问/forum-sign-in
时,这部分应在您的网站上显示为 go。 一旦他们登录,它将自动触发 sso 流,如果他们之前已经登录,这将自动发生。
auth.onAuthStateChanged(async user => {
if (!user)
//this means user was not already signed in.
//allow the user to sign in like normal
//this callback will be called again once they do
return
//generate firebase auth token
const idToken = await user.getIdToken(true)
//get the search params that discourse sent
//if you're in react you should probably do useSearchParams or something
const params = new URLSearchParams(document.location.search)
const discoursePayload = params.get('sso')
const discourseSignature = params.get('sig')
const response = await fetch(`https://MY-BACKEND.com/discourseAuth`, {
method: 'POST', //sending a json payload
mode: 'cors', //work with another domain
cache: 'no-cache', //send every time
headers: { 'Content-Type': 'application/json' }, //sending a json payload
body: JSON.stringify({
discoursePayload,
discourseSignature,
idToken
})
})
const json = await response.json()
if (json?.redirectUrl)
//lets go back to forum with credentials in the redirectUrl
//we're doing a custom redirect instead of a redirect-follow which is easier for security
window.location.replace(json.redirectUrl)
else
//something went wrong
throw new Error(`Redirect URL not found in response - ${response.status} - ${json}`)
})
server
- 身份验证处理程序我在这里使用 firebase 函数,但只要您有权访问firebase-admin
,您就可以使用您喜欢的任何后端。 此示例还包括一些用于用户名等的 Firestore 内容,这不是必需的。
import DiscourseSSO from 'discourse-sso'
import * as admin from 'firebase-admin'
import * as functions from 'firebase-functions'
const auth = admin.auth()
const firestore = admin.firestore()
const discourse = new DiscourseSSO(ssoSecret)
export const discourseAuth = functions.https.onRequest((req, res) => {
if (!handleCors(req, res))
return
//1. validate discourse payload
const { idToken, discoursePayload, discourseSignature } = req.body
if (!discourse.validate(discoursePayload, discourseSignature))
return res.status(401).send(`Bad Discourse Payload: ${origin}`)
//2. validate user
const decodedClaims = await auth.verifyIdToken(idToken).catch()
if (!decodedClaims)
return res.status(401).send(`Bad Id Token: ${idToken}`)
const { uid } = decodedClaims
const user = await auth.getUser(uid).catch()
if (!user)
return res.status(401).send(`User Not Found: ${uid}`)
//3. get user firestore (optional)
const firestoreDoc = await firestore.collection('users').doc(uid).get()
const userData = firestoreDoc.data()
//4. build discourse auth body
const q = discourse.buildLoginString({
nonce: discourse.getNonce(discoursePayload),
external_id: uid,
email: user.email,
//optional
name: userData.displayName,
username: userData.username,
avatar_url:userData.avatar
})
const redirectUrl = `$https://forum.MY-SITE.com/session/sso_login?${q}`
res.status(200).send(JSON.stringify({ redirectUrl }))
const handleCors = (req, res) => {
const origin = req.headers.origin || req.header('origin')
if(origin != 'https://MY-SITE.com'){
res.status(401).send(`Bad Origin: ${origin}`)
return false
}
res.set('Access-Control-Allow-Origin', origin)
res.set('Access-Control-Allow-Credentials', 'true')
res.set('Access-Control-Allow-Headers', ['Content-Type'])
if (req.method === 'OPTIONS'){
res.status(200).end()
return false
}
return true
}
第二种方法是我遇到问题的方法,因为我的服务器与客户端和论坛位于不同的域中。 这意味着 Safari 会将 cookies 视为第三方,并在尝试传递它们时给您带来困难。 如果您不是这种情况,请查看此讨论和此GitHub 要点。 凭证都是一样的,唯一的区别是它们是如何传递的。 它是这样的:
site -> server
server -> site
forum -> server
server -> forum
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.