Vue.js oidc-client-ts UserManager events not firing
Asked Answered
S

1

6

I am currently building out a Vue3 SPA and trying to use the oidc-client-ts javascript library for authenticating a user. Up to this point, I have been successful at implementing the login/logout functionality with my existing code. The problem I am experiencing is that I am not able to hook into the addUserLoaded or addUserSignedIn events that the UserManager provides (https://authts.github.io/oidc-client-ts/classes/UserManagerEvents.html). One point to note is that when you land on my domain, we'll say myapp.mydomain.com, you are redirected to our identity server at myid.mydomain.com and then redirected back once login in successful.



On startup I've got the following

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);
  }                
});

This window.appConfig has the settings object for initializing the UserManager



This is my AuthService.ts class which I export. Note the events in the constructor that I am registering to the 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();
}


In my router/index.ts file I have the following

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;


My logout button is pretty basic

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();
      }
}

For example, one of the events that I would like to hook into is the addUserSignedIn event. However even when logging out and logging back in, the event does not fire. I would like to use this event to do some basic initialization of the user data. Any ideas on why these events are not firing?

Sickening answered 11/6, 2022 at 1:12 Comment(3)
Did you ever resolve this, I am facing the exact same issue?Unfounded
@Unfounded I just answered my own question. Please take a look at my answer and it should hopefully resolve any of your questions!Sickening
monitorSession: true is what will make the events to fire.Unfounded
S
3

Here is what ended up working for me. This is my AuthService.ts class that gets used around the codebase.

    // 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();
  }
}

My updated SSO settings object in my config.json that gets passed to the user manager

"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"
    }
  },

And for the sake of completeness, here are my callback.html and silent-redirect.html pages. These two files are placed in the top level public folder along with your config.json and oidc-client-ts.min.js file.

callback.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>

silent-renew.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>
Sickening answered 23/12, 2022 at 19:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.