Skip to content

User Session Avatar

Source Code - README

The component serves several purposes:

  • Nice and configurable interface for user interaction.
  • Inactivity detection in client side through SQ.sso.
  • Single Sign-Out implementation in client side.
  • Cookie validation (if using signed cookie)
    • Verifies signature in Cookie to avoid external tampering.
    • Verifies several claims from the application's access token against the cookie.

The readme contains complete documentation about its implementation and configuration, but see below recommended steps for implementation.

Prerequisites

The frontend environment requires:

  • npm for dependency management
  • a bundler (like webpack)
  • react and react-dom installed as dependencies
  • have configured Nexus registry for @sequel packages (see this)

Your application does not need to be react, in fact is a simple component that works smooth even in plain JS or Angular. But npm and a bundler are mandatory in order to properly use the component and its dependencies.

Integration

The component is implementation agnostic in the oidc client side. It requires some mappings in order to talk with it, but also allows developers to adjust the behaviour in some scenarios.

README

Do not forget to read the README of the component for more detailed documentation of the configuration and theme customization.

The authentication service can be implemented with oidc-client, your own implementation or whatever. Just ensure to follow the steps for implementing the callbacks and profile mapping.

The user object must be mapped from a profile or JWT claims. Ensure to ask for openid profile scopes (email just in case it does not include it) when asking for an access token. Then, the mapping is easy as:

Property Claim
userName sub
firstName given_name
lastName family_name
email email
oidc-client

When using oidc-client, you can map the properties like this:

const user = await userManager.getUser();
const userProp = {
  userName: user.profile.sub,
  firstName: user.profile.given_name,
  lastName: user.profile.family_name,
  email: user.profile.email,
};

Next step is to configure actions when several events happen (urls prop). First of all, configure the authentication url, this is implementation specific and it is out of scope. Next is to implement the renewSsoCookie, logOut and refreshSession.

  • renewSsoCookie: URL or Callback or empty.
    • URL: When the component needs to refresh the SQ.sso cookie, will call this endpoint using GET. It can also be a function that returns an string.
    • Callback: Calls the function to do the renew cookie operation. Can be a simple API call that does just exactly this.
    • Empty: Do not fill property to do nothing (not recommended).
  • logOut: URL or Callback or empty.
    • URL: When the component should make a log out process, redirects to that URL. It can also be a function that returns an string.
    • Callback: Calls the function to do the log out. Can be a simple API call or the end session flow of OIDC.
    • Empty: Does nothing (not recommended).
  • refreshSession: Callback or empty.
    • Callback: Calls the function to do a session refresh. That is: to remove all information and access tokens of the current user and ask for new ones without doing a log out/end session.
    • Empty: Does what logOut is configured to do (not recommended).
oidc-client

To implement logOut and refreshSession using oidc-client, you can do the following:

const logOut = async () => {
  // this redirects the webpage to the *end session* endpoint
  await userManager.signoutRedirect();
};
const refreshSession = async () => {
  // removes all access tokens and user information
  await userManager.removeUser();
  // log in process
  await userManager.clearStaleState(); // optional
  await userManager.signinRedirect();
};

For improved debug experience, the prop loggingEnabled can be set to true to log in console a lot of debug information around the internal process. If there is an issue in the component, it is better to include a trace of those logs.

Theming is out of scope of this document, please read the README for more detailed information.

angular.js

Integration using Angular.JS can be done with a simple directive, but depends on your implementation of the services which may affect the final implementation.

import { render } from 'react-dom';
import { UserSessionAvatar } from '@sequel/sequel.web.usersessionavatar';
import type OidcService from '.../oidc.service'; // your authentication service

const UserSessionAvatarDirective = ($q: ng.IQService, oidcService: OidcService): ng.IDirective => ({
  restrict: 'E',
  scope: {},
  template: '<div class="user-session-avatar" id="user-session-avatar"></div>',
  link(_, element) {
    $q.all([
      // this gets information about the user (can be the JWT parsed directly or whatever)
      // and also gets the URL to the Authority (which means to Sequel Authentication)
      oidcService.getAuthority(),
      oidcService.getUser(),
    ]).then(([authentication, user]) => {
      // do not render anything if there is no user, for example, while loggin in
      if (!user) return;

      render(
        (
          <UserSessionAvatar
            user={{
              userName: user.profile.sub,
              firstName: user.profile.given_name,
              lastName: user.profile.family_name,
              email: user.profile.email,
            }}
            urls={{
              authentication,
              async logOut() {
                await oidcService.logOut();
              },
              async renewSsoCookie() {
                await oidcService.renewSsoCookie();
              },
              async refreshSession() {
                await oidcService.refreshSession();
              },
            }}
            loggingEnabled={process.env.NODE_ENV === 'development'}
          />
        ), element[0],
      );
    });
  },
});

UserSessionAvatarDirective.$inject = ['$q', 'oidcService'];

export default UserSessionAvatarDirective;

Register the directive in the module and use it:

ngModule.directive('userSessionAvatar', UserSessionAvatar)
<div class="super-navbar">
    <!-- ... -->
    <user-session-avatar></user-session-avatar>
</div>

Angular

Integration with angular is similar to angular.js but it requires some steps in order to bridge between both.

First ensure to have configured "jsx": "react-jsx" in the tsconfig.json file. And do not forget to install @types/react and @types/react-dom.

Then create a new component, but rename the .component.ts to .component.tsx.

Using the @ViewChild annotation will bridge the react component into angular:

// user-session-avatar.component.tsx
import {
  Component,
  ViewContainerRef,
  AfterViewInit,
  ViewChild
} from "@angular/core";
import { render } from "react-dom";
import { forkJoin } from 'rxjs';
import type OidcService from '.../oidc.service'; // your authentication service

@Component({
  selector: "app-user-session-avatar",
  templateUrl: "./user-session-avatar.component.html",
  styleUrls: []
})
export class UserSessionAvatarComponent implements AfterViewInit {
  @ViewChild("container")
  container!: { nativeElement: HTMLDivElement };

  constructor(private readonly oidcService: OidcService) {}

  ngAfterViewInit(): void {
    this.renderComponent();
  }

  private renderComponent(): void {
    forkJoin(
      // this gets information about the user (can be the JWT parsed directly or whatever)
      // and also gets the URL to the Authority (which means to Sequel Authentication)
      this.oidcService.getAuthority(),
      this.oidcService.getUser(),
    ).subscribe(([authentication, user]) => {
      // do not render anything if there is no user, for example, while loggin in
      if (!user) return;

      render(
        (
          <UserSessionAvatar
            user={{
              userName: user.profile.sub,
              firstName: user.profile.given_name,
              lastName: user.profile.family_name,
              email: user.profile.email,
            }}
            urls={{
              authentication,
              logOut: async () => {
                await this.oidcService.logOut();
              },
              renewSsoCookie: async () => {
                await this.oidcService.renewSsoCookie();
              },
              refreshSession: async() => {
                await this.oidcService.refreshSession();
              },
            }}
          />
        ),
        this.container.nativeElement,
      );
    })
  }
}
<!-- user-session-avatar.component.html -->
<div class="user-session-avatar" #container></div>

React

Just use the component wherever you want to call it. Recommended to wrap the User Session Avatar inside another component to keep together the mappings and callbacks.

If want an example, checkout Administration's code.

Plain JS

Component documentation talks about this integration, and there are even a bundle for it. But it is deprecated and some time in the future will be removed. Do not rely on this build!

Appendix: Configure Nexus Registry

Near the package.json write the following contents inside .npmrc:

@sequel:registry=https://nexus.office.sbs/repository/npm/
strict-ssl=false