npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

trustgate-auth

v1.8.4

Published

Angular authentication library for browser applications that authenticate through TrustGate's OAuth 2.0 + PKCE flow with profile-based portal isolation.

Readme

@trustgate/auth

Angular authentication library for browser applications that authenticate through TrustGate's OAuth 2.0 + PKCE flow with profile-based portal isolation.

Purpose

Provides Angular portals with:

  • Profile-based OAuth 2.0 PKCE login flow integration
  • Automatic endpoint derivation from profile configuration (/api/portal-auth/{profile}/*)
  • Session management with HTTP-only cookie + session storage
  • Route protection via AuthGuard
  • Automatic JWT/session cookie inclusion in HTTP requests
  • Session expiry detection and automatic logout
  • Multi-portal isolation - each portal maintains separate session

Architecture

Angular Portal (Support, Merchant, etc.)
    ↓
@trustgate/auth service (configured with profile)
    ↓
Profile-based endpoints: /api/portal-auth/{profile}/start, /callback, /session, /logout
    ↓
TrustGate OAuth provider
    ↓
JWT token issued (stored in Authorization header + HTTP-only cookie)
    ↓
Portal requests protected API
    ↓
API validates JWT + checks permissions

Installation

npm install @trustgate/auth

Usage

Basic Setup

import { provideTrustGateTamAuth } from '@trustgate/auth';
import { bootstrapApplication } from '@angular/platform-browser';

const config = {
  profile: 'support',                    // Profile name from PortalAuthProfiles
  authBasePath: '/api/portal-auth',      // Base path to portal auth endpoints
  apiBase: 'http://localhost:5001'       // API base URL
};

bootstrapApplication(AppComponent, [
  provideTrustGateTamAuth(config),
  // ... other providers
]);

Configuration

Settings Object:

export interface TrustGateAuthConfig {
  // (required) Profile name - used to construct auth endpoints
  // Example: profile="support" → /api/portal-auth/support/start
  profile: string;

  // (required) Auth endpoint base path
  // Full endpoints: {apiBase}{authBasePath}/{profile}/start, etc.
  authBasePath: string;

  // (required) API base URL
  // Used for all backend API calls
  apiBase: string;

  // (optional) Where to redirect after login
  // Default: current page
  defaultReturnUrl?: string;

  // (optional) Where to redirect after logout
  // Default: /login
  logoutReturnUrl?: string;

  // (optional) Session check interval (ms)
  // Default: 60000 (1 minute)
  sessionCheckInterval?: number;

  // (optional) Enable debug logging
  // Default: false
  debug?: boolean;
}

Per-Environment Configuration

environment.ts (local development):

export const environment = {
  production: false,
  api: 'http://localhost:5001',
  trustgateAuth: {
    profile: 'support',
    authBasePath: '/api/portal-auth',
    apiBase: 'http://localhost:5001'
  }
};

environment.prod.ts (production):

export const environment = {
  production: true,
  api: 'https://api.example.com',
  trustgateAuth: {
    profile: 'support',
    authBasePath: '/api/portal-auth',
    apiBase: 'https://api.example.com'
  }
};

app.config.ts:

import { environment } from './environments/environment';

export const appConfig: ApplicationConfig = {
  providers: [
    provideTrustGateTamAuth(environment.trustgateAuth),
    // ... other providers
  ]
};

Route Protection

Use AuthGuard to protect routes:

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [AuthGuard]
  },
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [AuthGuard],
    data: { roles: ['admin'] }
  }
];

HTTP Interceptors

The library automatically registers interceptors for:

  • Adding session cookies to requests (withCredentials: true)
  • Detecting session expiry (401 responses)
  • Redirecting to login on authentication failures

No additional configuration required.

Session Management

Login

import { AuthService } from '@trustgate/auth';

@Component({
  selector: 'app-login',
  template: `<button (click)="login()">Login</button>`
})
export class LoginComponent {
  constructor(private auth: AuthService) {}

  login() {
    this.auth.login('/dashboard');  // Redirect to /dashboard after login
  }
}

Check Authentication Status

constructor(private auth: AuthService) {}

ngOnInit() {
  this.auth.isAuthenticated$
    .pipe(takeUntilDestroyed())
    .subscribe(isAuth => {
      if (isAuth) {
        console.log('User is logged in');
      } else {
        this.router.navigate(['/login']);
      }
    });
}

Get Current User

constructor(private auth: AuthService) {}

ngOnInit() {
  this.auth.currentUser$
    .pipe(takeUntilDestroyed())
    .subscribe(user => {
      if (user) {
        console.log('Current user:', user.name, user.email);
        console.log('Permissions:', user.permissions);
      }
    });
}

Logout

logout() {
  this.auth.logout('/login');  // Redirect to /login after logout
}

Session Storage Model

The package stores session data in browser sessionStorage (not localStorage):

sessionStorage:
  auth_token: JWT bearer token (for Authorization header)
  auth_session: Session ID (from HTTP-only cookie)
  auth_profile: Current profile (e.g., "support")
  auth_user: { name, email, roles, permissions, expiresAt }

Key Points:

  • sessionStorage is cleared when browser tab closes
  • Data is NOT persisted across tabs/refreshes
  • Each tab can have different auth state
  • Prevents accidental data leaks between tabs/windows

Multi-Portal Isolation

Each portal uses a different profile, enabling simultaneous login to multiple apps:

Support Portal:

provideTrustGateTamAuth({
  profile: 'support',
  authBasePath: '/api/portal-auth',
  apiBase: 'http://localhost:5001'
})

Merchant Portal (in different Angular app):

provideTrustGateTamAuth({
  profile: 'merchant',
  authBasePath: '/api/portal-auth',
  apiBase: 'http://localhost:5001'
})

Result:

  • Support Portal tab maintains auth_profile: "support" in sessionStorage
  • Merchant Portal tab maintains auth_profile: "merchant" in sessionStorage
  • Each tab independently manages login/logout
  • Session cookies named: tam-api.support.auth and tam-api.merchant.auth
  • No session mixing or interference

OAuth 2.0 PKCE Flow

The package implements RFC 7636 PKCE (Proof Key for Public Clients):

1. Portal generates random code_verifier
2. Portal computes code_challenge = BASE64URL(SHA256(code_verifier))
3. Portal redirects browser: GET /api/portal-auth/{profile}/start?code_challenge=...
4. Backend redirects to TrustGate OAuth provider
5. User logs in on TrustGate
6. TrustGate redirects back: GET /api/portal-auth/{profile}/callback?code=...
7. Backend exchanges code + code_verifier for JWT
8. JWT issued + HTTP-only cookie set
9. Browser redirected to returnUrl
10. Subsequent requests include HTTP-only cookie automatically

Security Benefits:

  • Code interception doesn't allow token exchange (requires code_verifier)
  • No tokens transmitted through browser history
  • Public clients (SPAs) are protected against authorization code attacks

Error Handling

constructor(private auth: AuthService) {}

ngOnInit() {
  this.auth.authError$
    .pipe(takeUntilDestroyed())
    .subscribe(error => {
      if (error) {
        console.error('Auth error:', error.message);
        // Handle error (show toast, redirect, etc.)
      }
    });
}

Common Scenarios

Redirect to Login on App Load

export class AppComponent implements OnInit {
  constructor(
    private auth: AuthService,
    private router: Router
  ) {}

  ngOnInit() {
    this.auth.isAuthenticated$
      .pipe(
        first(),
        tap(isAuth => {
          if (!isAuth && !this.isLoginPage()) {
            this.router.navigate(['/login']);
          }
        }),
        takeUntilDestroyed()
      )
      .subscribe();
  }

  private isLoginPage() {
    return this.router.url === '/login';
  }
}

Display User in Header

@Component({
  selector: 'app-header',
  template: `
    <div *ngIf="user$ | async as user">
      Welcome, {{ user.name }}!
      <button (click)="logout()">Logout</button>
    </div>
  `
})
export class HeaderComponent {
  user$ = inject(AuthService).currentUser$;

  logout() {
    inject(AuthService).logout();
  }
}

Protect Admin Routes

const routes: Routes = [
  {
    path: 'admin',
    component: AdminDashboardComponent,
    canActivate: [AuthGuard],
    data: { requiredPermissions: ['tam.admin'] }
  }
];

// AuthGuard checks currentUser$.permissions includes 'tam.admin'

Troubleshooting

Session Lost After Refresh

Cause: Login was never initiated (no PKCE flow completed)

Solution: Call auth.login() from login page, not programmatically

401 Responses Despite Being Logged In

Cause: JWT token expired, but HTTP-only cookie still valid

Solution: Library auto-refreshes. If problem persists, clear sessionStorage and re-login

Cannot Login to Multiple Portals Simultaneously

Cause: Using same profile in multiple tabs

Solution: Use different profiles: one app uses profile: "support", another uses profile: "merchant"

CORS Errors

Cause: API missing CORS headers or credentials handling

Solution: Verify API includes:

  • Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Origin: <portal-domain>
  • withCredentials: true in Angular HttpClient

API Reference

AuthService

Methods:

  • login(returnUrl?: string): void — Initiate OAuth login flow
  • logout(returnUrl?: string): void — Logout and clear session
  • refreshSession(): Observable<boolean> — Check session validity
  • hasPermission(permission: string): boolean — Check if user has permission
  • getToken(): string | null — Get current JWT token

Observables:

  • isAuthenticated$: Observable<boolean> — Current auth state
  • currentUser$: Observable<User | null> — Current user + permissions
  • authError$: Observable<AuthError | null> — Last auth error

User Model

export interface User {
  id: string;
  name: string;
  email: string;
  roles: string[];
  permissions: string[];
  profileName: string;
  expiresAt?: Date;
}

See Also

Notes

  • Browser apps should not implement local credential or MFA flows.
  • The application backend remains responsible for token validation and permission enforcement. For up-to-date authorization after role or permission changes, do not authorize from browser-stored user data.