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 permissionsInstallation
npm install @trustgate/authUsage
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:
sessionStorageis 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.authandtam-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 automaticallySecurity 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: trueAccess-Control-Allow-Origin: <portal-domain>withCredentials: truein Angular HttpClient
API Reference
AuthService
Methods:
login(returnUrl?: string): void— Initiate OAuth login flowlogout(returnUrl?: string): void— Logout and clear sessionrefreshSession(): Observable<boolean>— Check session validityhasPermission(permission: string): boolean— Check if user has permissiongetToken(): string | null— Get current JWT token
Observables:
isAuthenticated$: Observable<boolean>— Current auth statecurrentUser$: Observable<User | null>— Current user + permissionsauthError$: 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
- TrustGate.Authorization Package
- Portal Auth Implementation
- Session Isolation Architecture
- TAM Portal Example
- keeps MFA UI out of business applications while still centralizing the component
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.
