0

我有一个使用redux-oidc的登录系统,其中keycloak作为 ID 管理器。该应用程序的登录页面是登录屏幕。当用户单击登录按钮时,他们会被引导使用 keycloak 登录。在登录时,它们会被重定向回我们的应用程序。问题是登录屏幕在被带到应用程序之前会短暂闪烁。我们正试图摆脱这种情况。以下是页面从重定向加载后触发的 redux 操作:

在此处输入图像描述

我可以在这里看到登录屏幕闪烁的原因 -LOADING_USER设置state.oidc.isLoadingUser为 true,但USER_EXPIRED将其设置为 false。然后USER_FOUND设置用户,我们LOGIN_SUCCESS将所有用户/会话数据保存在存储中并调用state.auth.authenticatedtrue。在从重定向加载页面时,有一个短暂的时刻,我没有用户数据和身份验证,并且无法知道 oidc 客户端仍在加载我的用户。

一旦已经登录,页面重新加载就不是这种情况。重新加载时,我得到这个:

在此处输入图像描述

这是有道理的,因为用户数据已经存在,所以要么state.oidc.isLoadingUser或要么state.oidc.user是真的。但我希望这在重定向的第一页加载时是相同的,因为用户数据是使用redirect_uri. 看一下代码,我们在 store 定义后立即调用redux-oidc's :loadUser

// in index.js 
const store = createStore(rootReducer, middleware)
loadUser(store, userManager);

userManager据我所知,我们正在以一种非常标准的方式进行设置:

const userManagerConfig = {
  authority: `${window.location.protocol}//${window.location.hostname}:8081/auth/realms/master`,
  client_id: "myclientid",
  redirect_uri: `${window.location.protocol}//${window.location.hostname}${
    window.location.port ? `:${window.location.port}` : ""
  }${window.location.pathname}`,
  response_type: "code",
  scope: "openid",
  automaticSilentRenew: true,
  accessTokenExpiringNotificationTime: 20,
  revokeAccessTokenOnSignout: true,
  post_logout_redirect_uri: `${window.location.protocol}//${
    window.location.hostname
  }${window.location.port ? `:${window.location.port}` : ""}`
};

回调组件是它LoginPage本身:

// LoginScreen.tsx

<CallbackComponent userManager={userManager} >
  <div>
    <h3>Log me in Scotty</h3>
    <Button onClick={() => { userManager.signinRedirect()} } >
      Login
    </Button>
  </div>
</CallbackComponent>

在应用程序中,我们仅在用户为authenticated. 否则,我们渲染登录屏幕:

// App.tsx
const App = () => {

   if (authenticated) {
     return (
       <Authenticated_App_With_All_The_Routes />
     );
   }
   return <LoginPage />;

}

oidc 和 live 之间的连接authenticated在我们的 sagas 中USER_FOUND被拦截,我们调用后续动作将有效负载传递给 store 并设置authenticated为 true。

也许这是 TMI,但我想给出这个问题的全部范围。我相信它的核心是,由于某种原因,redux-oidc 正在调用USER_EXPIRED然后调用USER_FOUND一次 keycloak 重定向回我们的应用程序。为什么?我如何跟踪我们仍在等待 redux-oidc 找到用户这一事实的页面加载?我需要让我的应用程序(和用户)知道您刚刚通过第三方登录,等待我们将您重定向到该应用程序。我是 OIDC 的新手,所以如果我遗漏了一些明显的问题,请原谅我。谢谢阅读。

4

1 回答 1

2

根据要求,我会回答我自己的问题。USER_EXPIRED我永远无法避免在登录之前触发的事实USER_FOUND。但是,一旦您从 OIDC 登录并被重定向回应用程序,URL 现在会附加/redirect?state=some_state_token&session_state=session_state_token&etc=etc.

我正在使用反应路由器,并且我利用了可以根据 url 呈现不同组件的事实。我在渲染我App实际涉及的<Switch>块的地方,如下所示:

// App.tsx
const App = () => {

   if (authenticated) {
     return (
       <Authenticated_App_With_All_The_Routes />
     );
   }
   return (
     <Switch>
       <Route path="/redirect">
         <LoginScreen redirect />
       </Route>
       <Route path="/">
         <LoginScreen />
       </Route>
     </Switch>
   )
}

因此,当用户到达主屏幕时,他们会得到常规的LoginPage. 当他们登录并从 OIDC 重定向回来时,他们实际上获得了带有redirectprop 的登录页面,这实际上将呈现登录页面,但它不是一个登录按钮,而是一个“重定向......” 信息:

// LoginScreen.tsx
const LoginScreen = ({ redirect }) => {

  if (!authenticated){
    return (
      <CallbackComponent userManager={userManager} >
        <div>
          {redirect && <div>Redirecting . . .</div>}
          {!redirect && <h3>Log me in Scotty</h3>
          <Button onClick={() => { userManager.signinRedirect()} } >
            Login
          </Button>}
        </div>
      </CallbackComponent>
    )
  }

  return <Redirect to="/" />

}

因此,一旦我们重新路由回来,我们就会看到“重定向......” message 片刻,直到LOGIN_SUCCESS触发,更改authenticated为 true,并且 LoginScreen 将应用程序重定向到主页。我认为这对于 UX 来说非常好,因为我们需要在 OIDC 进行重定向的短暂时刻展示一些有意义的东西。这似乎与最佳实践和许多企业级 OIDC UX 一致。

额外功劳:路由回您所在的页面

我们有一个要求,如果用户从不活动状态中退出,他们将被发送回登录屏幕,但他们仍然在他们所在的 url 上(例如,他们会在app.com/subsection,但已注销,他们会看到登录屏幕。从那里,单击登录屏幕会将他们撞到 oidc。在 redux-oidc 中userManagerConfig

redirect_uri: `${window.location.protocol}//${window.location.hostname}${
    window.location.port ? `:${window.location.port}` : ""
  }/redirect${
    window.location.pathname.split("/")[1]
      ? `?from=${window.location.pathname.split("/")[1]}`
      : ""
  }`

这样做的目的是,如果他们在尝试登录时位于某个子路由上,则会将?from=subroute参数添加到重定向 uri。然后在登录重定向中:

  return (
    <Redirect
      to={`/${new URLSearchParams(window.location.search).get("from") || ""}`}
    />
  );

OIDC 会记住他们的位置,将其作为to路径中 prop 的一部分传递,用户最终会登录并返回他们之前所在的页面。这个特定的例子只考虑了第一个子路径(在前两个之间/),但是这可以定制让你回到你原来的位置——如果你的应用程序使用深度链接可能会很棘手。您还必须小心,不要在/redirect路由中捕获任何 404 页面。

于 2021-03-09T16:57:19.657 回答