Angular 带有身份服务器和 oidc-client-js 的 SPA:是否有一种模式可以在应用程序启动时触发登录(不使用登录按钮)?

Angular SPA with identity server and oidc-client-js: is there a pattern to trigger login when the application starts (without using a login button)?

我正在使用 angular 8 SPA,我正在尝试使用 oidc-client-js 库来处理用户身份验证。身份提供者是使用身份服务器4实现的。

我们期望的用户体验如下:当 SPA 在浏览器中加载时,不需要任何类型的用户交互,登录流程就会启动。换句话说,我们希望自动触发登录流程,避免在应用程序的第一个视图中使用显式登录按钮。

AppComponent 是我们应用程序中加载的第一个组件,它具有以下初始化方法:

ngOnInit() {
    this.authService.login();
  }

身份验证服务登录正在执行以下操作:

login() {
        return this.userManager.signinRedirect();
    }

通过这样做,我们能够成功地将用户重定向到身份服务器登录表单。提交登录表单后,身份服务器将用户重定向回配置的重定向 uri (/signin-callback) 上的 SPA 应用程序。

那时我们想加载一个能够完成登录流程的组件。该组件应在其 ngOnInit 方法中调用身份验证服务的以下方法:

completeLogin() {
        return this.userManager.signinRedirectCallback().then(user => {
          console.log("Login has been completed.", user);
          return user;
        });
    }

不幸的是,这种方法 工作,因为当身份服务器重定向回 SPA 应用程序时(提交登录表单后),AppComponent 被加载到浏览器和登录流程再次启动,无限重定向循环开始,直到浏览器最终崩溃。

我们有什么办法可以做到这一点吗?

补充说明:

2020 年 9 月 15 日更新

我发现处理此问题的最简单方法是在 static HTML 页面 内执行 sigin 重定向回调,即 不是 angular 应用程序的一部分。你可以找到一个例子 in the sample folder of the oidc client js repository

要求

完成这项工作有两个主要方面。它真的是一种设计模式,可以用任何语言实现:

  • 根据您是否可以从 API 获取数据来触发登录重定向
  • 将登录响应作为页面加载的一部分进行处理,然后使令牌可用于调用 API,避免进一步重定向

您必须了解 Angular 的细节,因为我不知道该框架。我希望这能给你一些有用的建议。

我的资源

下面的代码使用纯 Typescript,您需要将其转换为您首选的 Angular 语法。

我的博客还有一些更高级的示例(如果有用的话),例如静默令牌更新 - 以及 Quick Start Page 您可以在其中 运行 具有上述行为的在线 React 示例。

在你的 auth.service 中你可能有类似这两个函数的东西:

private checkUser = (user : User): boolean => {
    return !!user && !user.expired;
}

public isAuthenticated = (): Promise<boolean> => {
    return this._userManager.getUser()
    .then(user => {
      if(this._user !== user){
        this._loginChangedSubject.next(this.checkUser(user));
      }

      this._user = user;

      return this.checkUser(user);
    })
}

然后而不是调用

ngOnInit() {
    this.authService.login();
}

你可以使用这个:

ngOnInit() {
    this.authService.isAuthenticated().then((isAuthenticated) => {
    if (!isAuthenticated) {
        this.authService.login();
    }
});
}