Skip to content

keycloak-express 1.0.0-alpha.2

Install from the command line:
Learn more about npm packages
$ npm install @bcgov/keycloak-express@1.0.0-alpha.2
Install via package.json:
"@bcgov/keycloak-express": "1.0.0-alpha.2"

About this version

BCGov SSO Keycloak Integration

Lifecycle:Experimental License

NodeJS Typescript Express


TL/DR:

  1. Install package by following the steps at Installing the Package.
  2. Set up the package by following the steps at Getting Started with the Integration.
  3. For use with @bcgov/keycloak-react.

Table of Contents

General Information

  • For running on a NodeJS:18 Express API.
  • For Keycloak Gold Standard.
  • Works with Vanilla JavaScript or Typescript 5.
  • For use with @bcgov/keycloak-react


Installing the Package

  1. Set up a personal access token (classic) with the read:packages permission. Suggested name: Read Packages. Be sure to copy the access token.

  1. Run the following command to log into NPM where Username is your GitHub username (lowercase), and Password is the personal access token:
npm login --scope=@bcgov --registry=https://npm.pkg.github.com

  1. Run the install command from the Packages page.

Return to Top


Getting Started with the Integration

  1. Add import const { keycloak } = require('@bcgov/keycloak-express'); or import { keycloak } from '@bcgov/keycloak-express'; to the top of the file that defines the express app. Add keycloak(app); below the definition of the express app, where app is defined by express().

Example:

import express from 'express';
import { keycloak } from '@bcgov/keycloak-express';

// Define Express App
const app = express();

// Initialize Keycloak(app: express.Application, options?: KeycloakOptions).
keycloak(app);
  1. Add the required environment variables from the Environment Variables section below.

Return to Top


Environment Variables

# Ensure the following environment variables are defined on the container.

ENVIRONMENT= # (local only) Set to 'local' when running container locally.
FRONTEND_PORT= # (local only) Port of the frontend application.
PORT= # (local only) Port of the backend application.

FRONTEND_URL= # (production only) URL of the frontend application.
BACKEND_URL= # (production only) URL of the backend application.

SSO_CLIENT_ID= # Keycloak client_id
SSO_CLIENT_SECRET= # Keycloak client_secret
SSO_AUTH_SERVER_URL= # Keycloak auth URL, see example below.
# https://dev.loginproxy.gov.bc.ca/auth/realms/standard/protocol/openid-connect

Return to Top


Module Exports

These are the functions and types exported by the @bcgov/keycloak-express module.

import {
  keycloak, // Initializes the keycloak service in your express app.
  protectedRoute, // Middleware function used for authentication and authorization.
} from '@bcgov/keycloak-express';

// Types:
function keycloak(app: Application, options?: KeycloakOptions): void; // Application from 'express'.
function protectedRoute(roles?: string | string[], options?: { requireAllRoles?: boolean }): RequestHandler;

// Typescript Types
import {
  KeycloakUser, // Base type for req.user
  KeycloakIdirUser, // User types specific to Idir users
  KeycloakBCeIDUser, // User types specific to BCeID users
  KeycloakGithubUser, // User types specific to Github users
  KeycloakOptions, // Type of optional second parameter for keycloak
} from '@bcgov/keycloak-express';

// Types:
type KeycloakIdirUser = {
  identity_provider: "idir";
  idir_user_guid: string;
  idir_username: string;
  given_name: string;
  family_name: string;
};

type KeycloakBCeIDUser = {
  identity_provider: "bceidbasic" | "bceidbusiness" | "bceidboth";
  bceid_user_guid: string;
  bceid_username: string;
  bceid_business_name?: string;
};

type KeycloakGithubUser = {
  identity_provider: "githubbcgov" | "githubpublic";
  github_id: string;
  github_username?: string;
  orgs?: string;
  given_name?: string;
  family_name?: string;
  first_name?: string;
  last_name?: string;
};

type KeycloakUser = {
  name?: string;
  preferred_username: string;
  email: string;
  display_name: string;
  client_roles?: string[];
  scope?: string;
} & (KeycloakIdirUser | KeycloakBCeIDUser | KeycloakGithubUser);

interface KeycloakOptions {
  afterUserLogin?: (userInfo: KeycloakUser) => void;
}

Return to Top


Initialization Options

Optional second parameter to the keycloak() function.

Currently supports the following functionality:

/*
 * A function that will be passed the userInfo defined as the
 * KeycloakUser type or the same as req.user.
 *
 * Use cases:
 * - Add user to database upon first login.
 * - Update a last login field in database.
 */
afterUserLogin?: (userInfo: KeycloakUser) => void;

Example usage:

import express from 'express';
import { keycloak } from '@bcgov/keycloak-express';
import { activateUser } from './src/utils'; // External function (not provided by keycloak-express).

// Define Express App
const app = express();

// Initialize Keycloak.
const keycloakOptions = { afterUserLogin: activateUser };
keycloak(app, keycloakOptions);

Return to Top


Authentication Flow

The Keycloak Authentication system begins when the user visits the frontend application.

  1. The user visits the frontend of the application. Here, the KeycloakWrapper component initializes and checks the URL for a query parameter named token.
  • If the token query parameter is found:

    • The component strips the URL of the access token.
    • The user's information is set into the state using the token.
    • The user can now access the frontend of the application.
  • If the token query parameter is not found, the component checks if the user is logged in by using the refresh token to get a new access token by communicating with the /api/oauth/token endpoint.

    • If the refresh token exists and is valid, the user can now access the frontend of the application without seeing the login button, as their session is authenticated through the refresh token.
    • If the refresh token doesn't exist or is invalid, the login button is displayed.
  1. When the user clicks the login button, they are routed to the /api/oauth/login endpoint via a proxy pass, which then redirects them to the Keycloak login page.

  2. Upon successful login at the Keycloak login page, Keycloak redirects the user to the /oauth/login/callback endpoint.

  3. The authentication code returned by the callback endpoint is used to retrieve the access token and the refresh token for the user.

  4. The user is redirected back to the frontend with the access token included as a token query parameter and the refresh token set as an httpOnly cookie.

  5. The KeycloakWrapper component re-initiates and the process repeats from step 1, this time with the token query parameter available.

Return to Top


Authentication on an Endpoint

Require keycloak authentication before using an endpoint. Import protectedRoute from @bcgov/keycloak-express and add as middleware.

import { protectedRoute } from '@bcgov/keycloak-express';

app.use("/users", protectedRoute(), usersRouter);

// Users must have 'Member' role.
app.use("/post", protectedRoute('Member'), postRouter);

// Users must have BOTH 'Member' and 'Commenter' roles.
app.use("/comment", protectedRoute(['Member', 'Commenter']), commentRouter);

// Users must have EITHER 'Member' or 'Verified' role.
app.use("/vote", protectedRoute(['Member', 'Verified'], { requireAllRoles: false }), voteRouter);

Return to Top


Authorization on an Endpoint

Get the keycloak user info in a protected endpoint.
IMPORTANT: req.user is either populated or null and the req.user.client_roles property is either a populated array or undefined.

Example within a controller of a protected route:

const user = req.user;
if (!user) return res.status(404).send("User not found.");
else {
  if (!req.user?.client_roles?.includes('Admin'))
    return res.status(403).send('User must be Admin.');
  // Do something with user.
}

For all user properties reference SSO Keycloak Wiki - Identity Provider Attribute Mapping.
Example IDIR req.user object (Typescript Type is KeycloakUser & KeycloakIdirUser):

{
  "idir_user_guid": "W7802F34D2390EFA9E7JK15923770279",
  "identity_provider": "idir",
  "idir_username": "JOHNDOE",
  "name": "Doe, John CITZ:EX",
  "preferred_username": "a7254c34i2755fea9e7ed15918356158@idir",
  "given_name": "John",
  "display_name": "Doe, John CITZ:EX",
  "family_name": "Doe",
  "email": "[email protected]",
  "client_roles": ["Admin"]
}

Return to Top

Details


Assets

  • keycloak-express-1.0.0-alpha.2.tgz

Download activity

  • Total downloads 3
  • Last 30 days 0
  • Last week 0
  • Today 0

Recent versions

View all