我们如何使用 Google 和 Outlook OAuth 弹出窗口
我们如何实现它
在Leave Me Alone,我们使用 Google 和 Microsoft OAuth 进行用户登录。为此,我们将用户重定向到相关的登录页面,用户输入其详细信息,然后被引导回我们的网站并登录。这样做的一个不幸的结果是,我们的分析报告显示大量引荐流量来自“accounts.google.com”和“login.microsoft.com”。
为了解决这个问题,与其进行重定向,不如为 OAuth 流程打开一个新窗口或弹出窗口。而且,这可能比重定向到其他地方更有利于用户体验。
我们如何实现它
我们使用Passport进行身份验证,因此当用户登录后被引导回我们的应用程序时,URL 包含我们需要的一些参数,包括我们用来在服务器上对其进行身份验证的令牌。
由于我们想要使用弹出窗口,因此我们需要在流程中间添加一个步骤来捕获重定向、检索 URL 参数、关闭弹出窗口并在打开的窗口(而不是弹出窗口)中使用令牌。
我们允许用户使用 Google 和 Outlook 登录,两者的实现方式相同。为了方便阅读,本示例将以 Google 为例。
步骤 1:打开弹出窗口
要打开新窗口,我们使用Window.open()函数,传入 Passport 登录 URL(本例中为 /auth/google),它会在新窗口中打开“登录到 Leave Me Alone with Google”页面。我们还会为窗口命名,并传入我们想要的功能。
我们分配窗口引用并记录之前的 URL,这样即使用户再次尝试点击登录按钮,即使登录的是不同的提供商,系统也会使用或聚焦同一个窗口。我们不希望两个不同提供商的弹窗四处浮动,造成混淆。
最后,我们添加一个消息事件监听器,因为弹出窗口将在完成时发送 URL 参数和身份验证令牌。
let windowObjectReference = null; | |
let previousUrl = null; | |
const openSignInWindow = (url, name) => { | |
// remove any existing event listeners | |
window.removeEventListener('message', receiveMessage); | |
// window features | |
const strWindowFeatures = | |
'toolbar=no, menubar=no, width=600, height=700, top=100, left=100'; | |
if (windowObjectReference === null || windowObjectReference.closed) { | |
/* if the pointer to the window object in memory does not exist | |
or if such pointer exists but the window was closed */ | |
windowObjectReference = window.open(url, name, strWindowFeatures); | |
} else if (previousUrl !== url) { | |
/* if the resource to load is different, | |
then we load it in the already opened secondary window and then | |
we bring such window back on top/in front of its parent window. */ | |
windowObjectReference = window.open(url, name, strWindowFeatures); | |
windowObjectReference.focus(); | |
} else { | |
/* else the window reference must exist and the window | |
is not closed; therefore, we can bring it back on top of any other | |
window with the focus() method. There would be no need to re-create | |
the window or to reload the referenced resource. */ | |
windowObjectReference.focus(); | |
} | |
// add the listener for receiving a message from the popup | |
window.addEventListener('message', event => receiveMessage(event), false); | |
// assign the previous URL | |
previousUrl = url; | |
}; |
let windowObjectReference = null; | |
let previousUrl = null; | |
const openSignInWindow = (url, name) => { | |
// remove any existing event listeners | |
window.removeEventListener('message', receiveMessage); | |
// window features | |
const strWindowFeatures = | |
'toolbar=no, menubar=no, width=600, height=700, top=100, left=100'; | |
if (windowObjectReference === null || windowObjectReference.closed) { | |
/* if the pointer to the window object in memory does not exist | |
or if such pointer exists but the window was closed */ | |
windowObjectReference = window.open(url, name, strWindowFeatures); | |
} else if (previousUrl !== url) { | |
/* if the resource to load is different, | |
then we load it in the already opened secondary window and then | |
we bring such window back on top/in front of its parent window. */ | |
windowObjectReference = window.open(url, name, strWindowFeatures); | |
windowObjectReference.focus(); | |
} else { | |
/* else the window reference must exist and the window | |
is not closed; therefore, we can bring it back on top of any other | |
window with the focus() method. There would be no need to re-create | |
the window or to reload the referenced resource. */ | |
windowObjectReference.focus(); | |
} | |
// add the listener for receiving a message from the popup | |
window.addEventListener('message', event => receiveMessage(event), false); | |
// assign the previous URL | |
previousUrl = url; | |
}; |
为了让窗口以弹出窗口而不是新选项卡的形式打开,我们必须请求这些功能menubar=no,toolbar=no
。
步骤 2:在弹出窗口中获取 OAuth 回调参数
OAuth 流程完成后,Google 会将用户重定向到一个回调 URL。通常,这是一个执行 Passport 身份验证的服务器路由。由于身份验证是在弹窗中进行的,因此我们在应用中使用了一个页面,该页面在加载时会抓取搜索参数并将其发送给父级。
此回调页面使用React Use Effect Hook,该 Hook 在页面加载时执行。我们获取包含身份验证令牌的 URL 参数,并使用Window.postMessage()将其发送到正在打开的窗口(父窗口) 。
步骤 3:验证用户身份并重定向到应用程序
OAuth 流程几乎完成,弹出窗口现已关闭,我们只需要在我们的服务器上对用户进行身份验证。
出于安全考虑,接收消息函数需要检查消息来源,以确保其来自同一域。在编写代码时,我们意识到一些 Chrome 开发者工具使用来自同一域的 postMessage() 函数,因此在尝试提取有效载荷之前,我们也会检查消息来源。
一旦我们有了 OAuth 参数,我们就会将用户重定向到我们自己的身份验证端点,以便我们可以使用 Passport 进行身份验证和登录。
完成的!
这个过程非常简单,我们所做的就是在 OAuth 流中添加一个中间步骤来传递回调参数。
可能有很多实现,但对于我们来说,使用 React.js 是最快、最简单的。
希望这对您有所帮助或为您自己的解决方案提供一些启发。
如果您有任何问题或建议,请告诉我们!
文章来源:https://dev.to/dinkydani21/how-we-use-a-popup-for-google-and-outlook-oauth-oci