1

我将 angular-oauth2-oidc 库与带有 keycloak 的隐式流结合使用。

this.oauthService.initImplicitFlow();登录或注销都没有问题this.oauthService.logOut();

但是,我想知道,是否可以检查我是否已经在其他地方登录?(不同的域但使用相同的 keycloak 服务器)

我搜索了 angular-oauth2-oidc 的文档,但没有找到。

我已经尝试过this.oauthService.initImplicitFlowInternal();,但它似乎与this.oauthService.initImplicitFlow();

更新:

我已经使用silentRefresh 获得了令牌,但是,它似乎创建了另一个导航组件(或者可能是一个全新的页面)。问题是,这个“新”页面被隐藏了,我只能看到“旧”页面。 在此处输入图像描述

我使用时间戳为导航组件创建了一个 id,如您所见,实际上有两个 id。

“新”组件获得了令牌,因此它已登录(但该组件被隐藏了!)。

“旧”组件不知道令牌,因此它仍然在导航栏上显示“登录”,除非我手动创建一个按钮并在单击时从会话存储中获取令牌。

更新:第二个组件是 iframe。我会探索更多。

4

2 回答 2

1

如果您还没有设置静默刷新this.oauthService.silentRefresh(),我建议您使用.

从技术上讲,这并不能直接回答您的问题,因为它不会“检查”您是否已登录,而是直接将您登录到应用程序中。如果它失败(你还没有登录),它会拒绝从silentRefresh().

作为参考,您可以查看我的示例存储库,它具有此登录流程,即使登录发生在其他地方,它也支持静默登录用户。这是本质:

// 0. LOAD CONFIG:
// First we have to check to see how the IdServer is
// currently configured:
this.authService.loadDiscoveryDocument()

  // 1. HASH LOGIN:
  // Try to log in via hash fragment after redirect back
  // from IdServer from initImplicitFlow:
  .then(() => this.authService.tryLogin())

  .then(() => {
    if (!this.authService.hasValidAccessToken()) {

      // 2. SILENT LOGIN:
      // Try to log in via silent refresh because the IdServer
      // might have a cookie to remember the user, so we can
      // prevent doing a redirect:
      this.authService.silentRefresh()
        .catch(result => {
          // Subset of situations from https://openid.net/specs/openid-connect-core-1_0.html#AuthError
          // Only the ones where it's reasonably sure that sending the
          // user to the IdServer will help.
          const errorResponsesRequiringUserInteraction = [
            'interaction_required',
            'login_required',
            'account_selection_required',
            'consent_required',
          ];

          if (result && result.reason && errorResponsesRequiringUserInteraction.indexOf(result.reason.error) >= 0) {

            // 3. ASK FOR LOGIN:
            // At this point we know for sure that we have to ask the
            // user to log in, so we redirect them to the IdServer to
            // enter credentials:
            this.authService.initImplicitFlow();
          }
        });
    }
});

但是,当您在其他地方登录时,您的问题的直接答案可能是您无法收到推送通知(“检查”),因为要做到这一点,您需要知道您是谁(登录)以建立会话检查。(但是,当您在其他地方注销sessionChecksEnabled时,配置确实可以帮助您“检查” ,请参阅这个最近的问题

于 2018-08-09T12:29:07.797 回答
1

正如我在问题中提到的,获取是否登录的相对状态是没有问题的。问题在于 iframe,因为只有 iframe 知道发生了什么(因为它是重定向的 url!)。让我的主应用程序也响应。我做了一些调整和“黑客”。

这是我如何让它工作的。它会检测我是否在其他地方登录并自动登录。如果我在另一个站点注销,它也会在我的应用程序中注销。

* 检测我是否在其他地方登录 *
我创建了一个方法'checkLoginState',它负责检查我的会话中是否有令牌或检查服务器是否我已经登录。
间隔只是定期检查如果 iframe 获得了令牌。

checkLoginState() {
    const claims = this.oauthService.getIdentityClaims();
    if (!claims) {
        if (this.ssoInterval) {
            // if we are waiting on response, return;
            return;
        }
        // try to get a token if already logged in somewhere else
        this.oauthService
            .loadDiscoveryDocument()
            .then(() => this.oauthService.tryLogin())
            .then(() => {
                if (!this.oauthService.hasValidAccessToken()) {
                    this.setupSSOInterval();
                    this.oauthService.silentRefresh().catch(err => {
                        // this will throws a time_out error as we don't have a 
valid token to refresh
                        // console.error('refresh error', err);
                        this.clearSSOInterval();
                    });
                }
            })
            .catch(e => {
                // console.log(e);
                // if not logged in anywhere, it will throw a token error.
                this.clearSSOInterval();
            });
        return;
    }
    if (this.oauthService.getIdTokenExpiration() < new Date().getTime()) {
        this.userService.removeToken();
        return this.logout();
    }
    this.isLoggedIn = true;
    this.userService.authenticateWithNID(claims['email']);
}
private setupSSOInterval() {
    this.ssoInterval = setInterval(() => {
        if (this.isLoggedIn) {
            clearInterval(this.ssoInterval);
        } else {
            this.checkLoginState();
        }
    }, 1000);
}
private clearSSOInterval() {
    if (this.ssoInterval) {
        clearInterval(this.ssoInterval);
    }
}

并在 ngOnInit() 中调用此方法;

* 检测我是否在其他地方注销 *
要检测我是否已注销,首先将其设置sessionChecksEnabled为 true(如 @Jeroen 所说)。然后监听会话存储的变化。(因为 iframe 会更新会话存储)

ngOnInit() {
    window.addEventListener(
        'storage',
        this.storageEventListener.bind(this)
    );
    // this is for handle the normal redirect when we login from this app
    this.oauthService.events.subscribe(({ type }: OAuthEvent) => {
        switch (type) {
            case 'token_received': {
                this.checkLoginState();
            }
        }
    });
    this.checkLoginState();
}
private storageEventListener(event: StorageEvent) {
    // if there is a session change and claims is missing, means I am no longer logged in
    if (event.storageArea === sessionStorage) {
        if (!sessionStorage.getItem('id_token_claims_obj')) {
            this.isLoggedIn = false;
        }
    }
}

请记住删除this.oauthService.loadDiscoveryDocumentAndTryLogin();构造函数方法中的 。如果您在其他站点注销,它将引发一些错误。(如果需要,您可以捕获错误,但内部调用了相同的方法checkloginState())。

我刚刚意识到我也可以使用会话存储侦听器进行登录检查(替换间隔)。但我会暂时离开它。

于 2018-08-10T07:14:08.570 回答