CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-oidc-client-ts

OpenID Connect (OIDC) & OAuth2 client library for TypeScript/JavaScript applications

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

user-tokens.mddocs/

User and Token Information

User profile and token data structures with comprehensive claims support and token lifecycle management for authenticated users.

Capabilities

User Class

Represents an authenticated user with profile information and tokens.

/**
 * Represents an authenticated user with profile and token information
 */
class User {
  constructor(args: {
    id_token?: string;
    session_state?: string | null;
    access_token: string;
    refresh_token?: string;
    token_type: string;
    scope?: string;
    profile: UserProfile;
    expires_at?: number;
    userState?: unknown;
    url_state?: string;
  });
  
  /** The raw ID token */
  readonly id_token?: string;
  /** Session state for session management */
  readonly session_state: string | null;
  /** The access token for API calls */
  readonly access_token: string;
  /** The refresh token for token renewal */
  readonly refresh_token?: string;
  /** Token type (usually "Bearer") */
  readonly token_type: string;
  /** Space-delimited scope values */
  readonly scope?: string;
  /** User profile extracted from ID token */
  readonly profile: UserProfile;
  /** Timestamp when access token expires */
  readonly expires_at?: number;
  /** Custom state data from authentication */
  readonly state: unknown;
  /** URL state parameter */
  readonly url_state?: string;
  
  /** Seconds until access token expires */
  readonly expires_in?: number;
  /** Whether the access token has expired */
  readonly expired?: boolean;
  /** Array of individual scope values */
  readonly scopes: string[];
  
  /** Serialize user to string for storage */
  toStorageString(): string;
  
  /** Create User from storage string */
  static fromStorageString(storageString: string): User;
}

User Profile Structure

Standard OIDC user profile with optional claims.

/**
 * User profile information from ID token and userinfo endpoint
 */
interface UserProfile extends IdTokenClaims {
  /** Subject identifier - unique user ID */
  sub: string;
  
  // Standard profile claims
  /** Full display name */
  name?: string;
  /** Given name (first name) */
  given_name?: string;
  /** Family name (last name) */
  family_name?: string;
  /** Middle name */
  middle_name?: string;
  /** Casual name or alias */
  nickname?: string;
  /** Preferred username for display */
  preferred_username?: string;
  /** Profile page URL */
  profile?: string;
  /** Picture/avatar URL */
  picture?: string;
  /** Website URL */
  website?: string;
  
  // Contact claims
  /** Email address */
  email?: string;
  /** Whether email is verified */
  email_verified?: boolean;
  /** Phone number */
  phone_number?: string;
  /** Whether phone number is verified */
  phone_number_verified?: boolean;
  
  // Personal information
  /** Gender */
  gender?: string;
  /** Date of birth (YYYY-MM-DD format) */
  birthdate?: string;
  /** Time zone */
  zoneinfo?: string;
  /** Locale/language preference */
  locale?: string;
  /** Last profile update timestamp */
  updated_at?: number;
  
  /** Postal address information */
  address?: OidcAddressClaim;
}

/**
 * Standard OIDC address claim structure
 */
interface OidcAddressClaim {
  /** Full mailing address */
  formatted?: string;
  /** Street address */
  street_address?: string;
  /** City or locality */
  locality?: string;
  /** State, province, prefecture, or region */
  region?: string;
  /** ZIP or postal code */
  postal_code?: string;
  /** Country name */
  country?: string;
}

ID Token Claims

JWT claims structure for ID tokens.

/**
 * Standard ID token claims from JWT
 */
interface IdTokenClaims extends JwtClaims {
  /** Subject identifier */
  sub: string;
  /** Authentication time */
  auth_time?: number;
  /** Authentication Context Class Reference */
  acr?: string;
  /** Authentication Methods References */
  amr?: string[];
  /** Authorized party */
  azp?: string;
  /** Access token hash */
  at_hash?: string;
  /** Code hash */
  c_hash?: string;
  /** Nonce value */
  nonce?: string;
}

/**
 * Base JWT claims structure
 */
interface JwtClaims {
  /** Issuer */
  iss: string;
  /** Subject */
  sub: string;
  /** Audience */
  aud: string | string[];
  /** Expiration time */
  exp: number;
  /** Issued at time */
  iat: number;
  /** Not before time */
  nbf?: number;
  /** JWT ID */
  jti?: string;
  
  /** Additional custom claims */
  [key: string]: unknown;
}

Standard OIDC Claims

Complete set of standard OIDC claims.

/**
 * Standard OIDC claims as defined in the specification
 */
interface OidcStandardClaims {
  /** Subject identifier */
  sub: string;
  /** Full display name */
  name?: string;
  /** Given name */
  given_name?: string;
  /** Family name */
  family_name?: string;
  /** Middle name */
  middle_name?: string;
  /** Nickname */
  nickname?: string;
  /** Preferred username */
  preferred_username?: string;
  /** Profile page URL */
  profile?: string;
  /** Picture URL */
  picture?: string;
  /** Website URL */
  website?: string;
  /** Email address */
  email?: string;
  /** Email verified flag */
  email_verified?: boolean;
  /** Gender */
  gender?: string;
  /** Date of birth */
  birthdate?: string;
  /** Time zone */
  zoneinfo?: string;
  /** Locale */
  locale?: string;
  /** Phone number */
  phone_number?: string;
  /** Phone verified flag */
  phone_number_verified?: boolean;
  /** Address */
  address?: OidcAddressClaim;
  /** Last update time */
  updated_at?: number;
}

Usage Examples

Working with User Information

import { UserManager, User } from "oidc-client-ts";

const userManager = new UserManager({
  authority: "https://demo.identityserver.io",
  client_id: "interactive.public",
  redirect_uri: "http://localhost:3000/callback",
  response_type: "code",
  scope: "openid profile email phone address",
});

// Get current user
const user = await userManager.getUser();
if (user && !user.expired) {
  console.log("User Information:", {
    id: user.profile?.sub,
    name: user.profile?.name,
    email: user.profile?.email,
    emailVerified: user.profile?.email_verified,
    picture: user.profile?.picture,
  });
  
  console.log("Token Information:", {
    accessToken: user.access_token,
    tokenType: user.token_type,
    expiresIn: user.expires_in,
    scopes: user.scopes,
    hasRefreshToken: !!user.refresh_token,
  });
}

Token Expiration Handling

import { UserManager, User } from "oidc-client-ts";

const userManager = new UserManager({
  // ... configuration
  automaticSilentRenew: true,
  accessTokenExpiringNotificationTimeInSeconds: 60,
});

// Monitor token expiration
userManager.events.addAccessTokenExpiring((user: User) => {
  console.log(`Access token expiring in ${user.expires_in} seconds`);
  
  // Could show notification to user
  showTokenExpirationWarning();
});

userManager.events.addAccessTokenExpired(() => {
  console.log("Access token has expired");
  
  // Redirect to login or attempt silent renewal
  handleTokenExpiration();
});

// Check token status
function checkTokenStatus(user: User | null) {
  if (!user) {
    return "Not authenticated";
  }
  
  if (user.expired) {
    return "Token expired";
  }
  
  if (user.expires_in && user.expires_in < 300) { // 5 minutes
    return "Token expiring soon";
  }
  
  return "Token valid";
}

Custom Claims Processing

import { UserManager } from "oidc-client-ts";

const userManager = new UserManager({
  authority: "https://your-provider.com",
  client_id: "your-client",
  redirect_uri: "http://localhost:3000/callback",
  response_type: "code",
  scope: "openid profile email custom_scope",
  
  // Load additional user info
  loadUserInfo: true,
  
  // Keep custom claims
  filterProtocolClaims: ["nbf", "jti", "auth_time", "nonce", "acr", "amr", "azp", "at_hash"],
  
  // Merge strategy for claims
  mergeClaimsStrategy: { array: "merge" },
});

// Access custom claims
const user = await userManager.getUser();
if (user?.profile) {
  // Standard claims
  const standardInfo = {
    name: user.profile.name,
    email: user.profile.email,
    picture: user.profile.picture,
  };
  
  // Custom claims (assuming your provider includes these)
  const customInfo = {
    department: (user.profile as any).department,
    roles: (user.profile as any).roles,
    permissions: (user.profile as any).permissions,
    tenant: (user.profile as any).tenant_id,
  };
  
  console.log("Standard claims:", standardInfo);
  console.log("Custom claims:", customInfo);
}

Address Information

import { User, OidcAddressClaim } from "oidc-client-ts";

function displayUserAddress(user: User) {
  const address = user.profile?.address;
  if (address) {
    console.log("User Address:");
    
    if (address.formatted) {
      console.log("Formatted:", address.formatted);
    } else {
      // Build address from components
      const components = [
        address.street_address,
        address.locality,
        address.region,
        address.postal_code,
        address.country,
      ].filter(Boolean);
      
      console.log("Address:", components.join(", "));
    }
  }
}

// Example address claim structure
const exampleAddress: OidcAddressClaim = {
  formatted: "123 Main St\nAnytown, CA 12345\nUSA",
  street_address: "123 Main St",
  locality: "Anytown", 
  region: "CA",
  postal_code: "12345",
  country: "USA",
};

User Storage and Serialization

import { User } from "oidc-client-ts";

// Manual user storage (normally handled by UserManager)
function storeUser(user: User) {
  const userString = user.toStorageString();
  localStorage.setItem("oidc.user", userString);
}

function loadUser(): User | null {
  const userString = localStorage.getItem("oidc.user");
  if (userString) {
    try {
      return User.fromStorageString(userString);
    } catch (error) {
      console.error("Failed to load user from storage:", error);
      localStorage.removeItem("oidc.user");
      return null;
    }
  }
  return null;
}

// Usage
const user = await userManager.getUser();
if (user) {
  storeUser(user);
}

const storedUser = loadUser();
if (storedUser && !storedUser.expired) {
  console.log("Loaded user from storage:", storedUser.profile?.name);
}

Install with Tessl CLI

npx tessl i tessl/npm-oidc-client-ts

docs

configuration.md

errors.md

events.md

index.md

oidc-client.md

storage.md

user-management.md

user-tokens.md

utilities.md

tile.json