Vue.js oidc-client-ts UserManager 事件未触发

Log*_*per 5 vue.js openid-connect oidc-client-js vuejs3

我目前正在构建一个 Vue3 SPA 并尝试使用 oidc-client-ts javascript 库来验证用户身份。到目前为止,我已经成功地使用现有代码实现了登录/注销功能。我遇到的问题是我无法连接 UserManager 提供的 addUserLoaded 或 addUserSignedIn 事件(https://authts.github.io/oidc-client-ts/classes/UserManagerEvents.html)。需要注意的一点是,当您登陆我的域(我们会说 myapp.mydomain.com)时,您将被重定向到位于 myid.mydomain.com 的身份服务器,然后在登录成功后重定向回来。



启动时我有以下内容

fetch("config.json", {'method': "GET"})
.then(response => { 
  if(response.ok) {
    response.json().then(appConfigData => {
      window.appConfig = appConfigData;
      createApp(App)
      .use(store)
      .use(router)
      .use(AccordionPlugin)
      .component("font-awesome-icon", FontAwesomeIcon)
      .mount("#app");
    });    
  } else{
      alert("Server returned " + response.status + " : " + response.statusText);
  }                
});
Run Code Online (Sandbox Code Playgroud)

这个window.appConfig具有用于初始化UserManager的设置对象



这是我导出的 AuthService.ts 类。请注意我在 UserManager 中注册的构造函数中的事件。

import { UserManager, WebStorageStateStore, User, UserManagerEvents } from "oidc-client-ts";
import jwt_decode from "jwt-decode";
import store from "@/store";

export default class AuthService {

private userManager: UserManager;

constructor(configuration: any) {

const IDP_URL: string = configuration.IDP_URL;
const REDIRECT_URI: string = configuration.REDIRECT_URI;
const AUTH_TOKEN_URI: string = configuration.AUTH_TOKEN_URI;
const CLIENT_ID: string = configuration.CLIENT_ID;
const SILENT_RENEW_REDIRECT_URI: string = configuration.SILENT_RENEW_REDIRECT_URI;
const POPUP_REDIRECT_URI: string = configuration.POPUP_REDIRECT_URI;

const settings: any = {
  userStore: new WebStorageStateStore({ store: window.localStorage }),
  authority: IDP_URL,
  client_id: CLIENT_ID,
  redirect_uri: REDIRECT_URI,
  popup_redirect_uri: POPUP_REDIRECT_URI,
  response_type: "code",
  automaticSilentRenew: true,
  silent_redirect_uri: SILENT_RENEW_REDIRECT_URI,
  scope: "openid profile ContactCenterApi.READ_WRITE",
  post_logout_redirect_uri: AUTH_TOKEN_URI,
  loadUserInfo: true
};

this.userManager = new UserManager(settings);

this.userManager.events.addUserLoaded(_args => {
  console.log("USER LOADED EVENT");
  debugger;
  this.userManager.getUser().then(usr => {
    store.dispatch("getProducts", usr?.profile.sub) // load the users products
  });
});

this.userManager.events.addUserSignedIn(() => {
  console.log("USER SIGNED IN EVENT");
  debugger;
  this.userManager.getUser().then(usr => {
    store.dispatch("getProducts", usr?.profile.sub) // load the users products
  });
});
}

public getUser(): Promise<User|null> {
  return this.userManager.getUser();
}

public async login(): Promise<void> {
  return this.userManager.signinRedirect();
}

public logout(): Promise<void> {
  return this.userManager.signoutRedirect();
}
Run Code Online (Sandbox Code Playgroud)


在我的 router/index.ts 文件中,我有以下内容

const auth = new AuthService(window.appConfig);

router.beforeEach(async (to, from, next) => {
  const user = await auth.getUser();
  const baseUri = window.appConfig.BASE_URI + "/#";

  console.log("redirectedUri = " + baseUri + to.fullPath);
  if (user) {
    if (!user.expired) {
      next();
    } else {
      sessionStorage.setItem("redirectedUri", baseUri + to.fullPath);
      await auth.login();
    }
  } else {
    sessionStorage.setItem("redirectedUri", baseUri + to.fullPath);
    await auth.login();
  }
});

export default router;
Run Code Online (Sandbox Code Playgroud)


我的注销按钮非常简单

import  AuthService  from "@/auth/AuthService";

onselect: async function(event: MenuEventArgs) {
      var authService = new AuthService(window.appConfig); // settings are stored in the window when the app mounts
      if(event.item.text === 'Logout') {
        await authService.logout();
      }
}
Run Code Online (Sandbox Code Playgroud)

例如,我想要挂钩的事件之一是 addUserSignedIn 事件。但是,即使注销并重新登录,该事件也不会触发。我想使用此事件对用户数据进行一些基本初始化。关于为什么这些事件没有触发有什么想法吗?

Log*_*per 2

这就是最终对我有用的东西。这是我的 AuthService.ts 类,在代码库中使用。

    // AuthService.ts
    import { UserManager, WebStorageStateStore, User } from "oidc-client-ts";
    import jwt_decode from "jwt-decode";
    import store from "@/store";
    
    let userManager: UserManager;
    
    const currentURL = document.location.origin;
    fetch(currentURL + "/config.json", { 'method': "GET" })
      .then(config => {
        config.json()
          .then(configuration => {
            configuration['SSO_SETTINGS']['userStore'] = new WebStorageStateStore({ store: window.localStorage });
            userManager = new UserManager(configuration['SSO_SETTINGS']);
    
            userManager.events.addUserLoaded(newUser => {
              console.log("USER LOADED EVENT");
              userManager.storeUser(newUser);
              userManager.getUser().then(usr => {
                store.dispatch("getUserInfo", usr?.profile.sub) // load the user from my api
              });
            });
    
            userManager.events.addAccessTokenExpired(() => {
              userManager.signoutRedirect();
            })
    
            userManager.events.addSilentRenewError(error => {
              console.log("ERROR RENEWING ACCESS TOKEN.");
              console.log(error);
            })
          })
      })

export default class AuthService {

  public static getUser(): Promise<User | null> {
    return userManager.getUser();
  }

  public static async login(): Promise<void> {
    return userManager.signinRedirect();
  }

  public static logout(): Promise<void> {
    localStorage.clear();
    sessionStorage.clear();
    return userManager.signoutRedirect();
  }
}
Run Code Online (Sandbox Code Playgroud)

我的 config.json 中更新的 SSO 设置对象已传递给用户管理器

"SSO_SETTINGS": {
    "userStore": "",
    "authority": "https://myauthorityurl.com",
    "client_id": "MyClientName",
    "redirect_uri": "http://localhost:8080/callback.html",
    "response_type": "code",
    "response_mode": "query",
    "automaticSilentRenew": true,
    "monitorSession": true,
    "silent_redirect_uri": "http://localhost:8080/silent-renew.html",
    "scope": "openid profile",
    "post_logout_redirect_uri": "http://localhost:8080",
    "loadUserInfo": true,
    "metadata": {
      "issuer": "https://myauthorityurl.com",
      "jwks_uri": "https://myauthorityurl.com/.well-known/openid-configuration/jwks",
      "authorization_endpoint": "https://myauthorityurl.com/connect/authorize",
      "end_session_endpoint": "https://myauthorityurl/Account/Logout",
      "token_endpoint": "https://iddev02.carexm.com/connect/token",
      "userinfo_endpoint": "https://iddev02.carexm.com/connect/userinfo"
    }
  },
Run Code Online (Sandbox Code Playgroud)

为了完整起见,这里是我的callback.html 和silent-redirect.html 页面。这两个文件与 config.json 和 oidc-client-ts.min.js 文件一起放置在顶级公共文件夹中。

回调.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Waiting...</title>
</head>
<body>
    <script src="oidc-client-ts.min.js"></script>
    <script>

        var config = fetch("config.json", {'method': "GET"})
        .then(config =>
        {
            config.json().then(settings => {
                var mgr = new oidc.UserManager(settings['SSO_SETTINGS']);
                mgr.settings.userStore = new oidc.WebStorageStateStore({ store: window.localStorage });
                mgr.signinRedirectCallback()
                .then(() =>
                { 
                    window.location.href = sessionStorage.getItem("redirectedUri") ?? "../";
                })
            })
        });

    </script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

静默更新.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Waiting...</title>
</head>
<body>
    <script src="oidc-client-ts.min.js"></script>
    <script>

        var config = fetch("config.json", {'method': "GET"})
        .then(config =>
        {
            config.json().then(settings => {
                var mgr = new oidc.UserManager(settings['SSO_SETTINGS']).signinSilentCallback();
            })
        });

    </script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)