npm package: @progress-chef/platform-auth-orchestration-service
Description: Centralized authentication orchestration service for Chef Platform UI applications. This service extracts and consolidates all authentication-related business logic, providing a clean API for both programmatic (standalone mode) and UI-driven (combined mod
Latest version: 0.0.4

# Platform Auth Orchestration Service

Centralized authentication orchestration service for Chef Platform UI applications. This service extracts and consolidates all authentication-related business logic, providing a clean API for both programmatic (standalone mode) and UI-driven (combined mode) authentication flows.

## Features

- **Standalone Mode**: Programmatic authentication API for headless/API usage
- **Combined Mode**: UI-driven authentication with organization/role selection
- **Multi-Auth Support**: Local (username/password) and OAuth/SAML authentication
- **Session Management**: Automatic token refresh and session warning
- **State Management**: Observable streams for reactive authentication state
- **Route Guards**: Functional guards for route protection
- **Org/Role Context**: Organization and role selection workflow
- **Storage Integration**: Seamless integration with platform storage service

## Installation

```bash
npm install @progress-chef/platform-auth-orchestration-service
```

## Dependencies

This service requires the following peer dependencies:

- `@angular/common` >= 17.3.0
- `@angular/core` >= 17.3.0
- `@angular/router` >= 17.3.0
- `@progress-chef/platform-storage-service` >= 1.0.3
- `@progress-chef/platform-shared-store` >= 1.0.12
- `@progress-chef/platform-refresh-token-service` >= 1.0.2
- `@progress-chef/platform-org-role-utils-service` >= 1.0.0
- `@progress-chef/platform-telemetry-proxy-service` >= 1.0.1
- `@progress-chef/platform-toast-notification-service` >= 1.0.10
- `@ngrx/store` >= 17.0.1
- `rxjs` ~7.8.0

## Usage

### Basic Setup

```typescript
import { AuthOrchestrationService } from '@progress-chef/platform-auth-orchestration-service';

@Component({
  selector: 'app-login',
  template: `...`
})
export class LoginComponent {
  constructor(private authService: AuthOrchestrationService) {}
}
```

### Standalone Mode (Programmatic Authentication)

#### Local Login (Username/Password)

```typescript
this.authService.loginWithCredentials({
  email: 'user@example.com',
  password: 'password123',
  baseUrl: 'https://api.chef.io'
}).subscribe(result => {
  if (result.success) {
    console.log('Login successful:', result.accessToken);
    // Navigate to org/role selection
  } else {
    console.error('Login failed:', result.error);
  }
});
```

#### OAuth Login (SSO)

```typescript
this.authService.loginWithOAuthToken({
  token: {
    accessToken: 'oauth-access-token',
    expireAt: 1234567890,
    refreshToken: 'oauth-refresh-token'
  },
  loginType: 'oauth', // or 'saml'
  baseUrl: 'https://api.chef.io'
}).subscribe(result => {
  if (result.success) {
    console.log('OAuth login successful');
  }
});
```

#### Set Organization and Role Context

```typescript
this.authService.setOrgRoleContext({
  organizationId: 'org-123',
  organizationName: 'My Organization',
  roleId: 'role-456',
  roleName: 'Administrator',
  baseUrl: 'https://api.chef.io'
}).subscribe(result => {
  if (result.success) {
    console.log('Context set successfully');
    // Navigate to main application
    this.router.navigate(['/dashboard']);
  }
});
```

### Combined Mode (UI-Driven Authentication)

#### Get Organizations for Dropdown

```typescript
this.authService.getOrganizations('https://api.chef.io')
  .subscribe(organizations => {
    this.organizationList = organizations;
  });
```

#### Get Roles for Selected Organization

```typescript
onOrganizationSelect(orgId: string) {
  this.authService.getRolesForOrganization(orgId, 'https://api.chef.io')
    .subscribe(roles => {
      this.roleList = roles;
    });
}
```

#### Get Global Roles

```typescript
this.authService.getGlobalRoles('https://api.chef.io')
  .subscribe(globalRoles => {
    this.globalRoleList = globalRoles;
  });
```

### State Management

#### Subscribe to Authentication State

```typescript
this.authService.authState$.subscribe(state => {
  console.log('Authenticated:', state.isAuthenticated);
  console.log('Org/Role Set:', state.isOrgRoleSet);
  console.log('Login Type:', state.loginType);
  console.log('Loading:', state.isLoading);
  console.log('Error:', state.error);
});
```

#### Subscribe to Org/Role Context

```typescript
this.authService.orgRoleContext$.subscribe(context => {
  if (context) {
    console.log('Organization:', context.organizationName);
    console.log('Role:', context.roleName);
  }
});
```

#### Check Authentication Status

```typescript
// Synchronous checks
const isAuthenticated = this.authService.isAuthenticated();
const isOrgRoleSet = this.authService.isOrgRoleSet();
const loginType = this.authService.getLoginType();
const authStatus = this.authService.getAuthStatus(); // 'authenticated' | 'unauthenticated' | 'loading' | 'error'
```

### Session Management

#### Start Automatic Token Refresh

```typescript
// Initialize session refresh after authentication
this.authService.startSessionRefresh({
  refreshInterval: 480000,  // 8 minutes (default)
  warningTimeout: 1800000,  // 30 minutes (default)
  autoRefresh: true         // Enable automatic refresh (default)
});
```

#### Stop Session Refresh

```typescript
this.authService.stopSessionRefresh();
```

#### Manual Token Refresh

```typescript
this.authService.refreshToken().subscribe(result => {
  if (result.success) {
    console.log('Token refreshed successfully');
  }
});
```

### Initialize from Storage (Page Refresh)

```typescript
ngOnInit() {
  this.authService.initializeFromStorage().subscribe(result => {
    if (result.isAuthenticated && result.isOrgRoleSet) {
      // User is fully authenticated, proceed to app
      this.router.navigate(['/dashboard']);
    } else if (result.isAuthenticated) {
      // User authenticated but needs to select org/role
      this.router.navigate(['/login/select-org-role']);
    } else {
      // User not authenticated, show login
      this.router.navigate(['/login']);
    }
  });
}
```

### Logout

```typescript
onLogout() {
  this.authService.logout('https://api.chef.io').subscribe(() => {
    console.log('Logout successful');
    this.router.navigate(['/login']);
  });
}
```

## Route Guards

### Basic Authentication Guard

```typescript
import { canActivateAuthGuard } from '@progress-chef/platform-auth-orchestration-service';

const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [canActivateAuthGuard]
  }
];
```

### Org/Role Required Guard

```typescript
import { canActivateAuthGuard } from '@progress-chef/platform-auth-orchestration-service';

const routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [canActivateAuthGuard],
    data: { requireOrgRole: true }
  }
];
```

### Org/Role Context Guard

```typescript
import { canActivateOrgRoleGuard } from '@progress-chef/platform-auth-orchestration-service';

const routes: Routes = [
  {
    path: 'organization-settings',
    component: OrgSettingsComponent,
    canActivate: [canActivateOrgRoleGuard]
  }
];
```

### Authentication Status Guard (No Redirect)

```typescript
import { canActivateAuthStatusGuard } from '@progress-chef/platform-auth-orchestration-service';

const routes: Routes = [
  {
    path: 'profile',
    component: ProfileComponent,
    canActivate: [canActivateAuthStatusGuard]
  }
];
```

## API Reference

### AuthOrchestrationService

#### Methods

- `loginWithCredentials(credentials: LoginCredentials): Observable<LoginResult>`
- `loginWithOAuthToken(params: OAuthLoginParams): Observable<LoginResult>`
- `setOrgRoleContext(context: OrgRoleContext): Observable<ContextRefreshResult>`
- `initializeFromStorage(): Observable<InitializationResult>`
- `logout(baseUrl: string): Observable<void>`
- `getOrganizations(baseUrl: string): Observable<Organization[]>`
- `getRolesForOrganization(orgId: string, baseUrl: string): Observable<Role[]>`
- `getGlobalRoles(baseUrl: string): Observable<Role[]>`
- `isAuthenticated(): boolean`
- `isOrgRoleSet(): boolean`
- `getLoginType(): 'local' | 'oauth' | 'saml' | null`
- `getAuthStatus(): AuthStatus`
- `startSessionRefresh(config?: SessionConfig): void`
- `stopSessionRefresh(): void`
- `refreshToken(context?: OrgRoleContext): Observable<RefreshResult>`

#### Observables

- `authState$: Observable<AuthenticationState>` - Authentication state stream
- `orgRoleContext$: Observable<OrgRoleContext | null>` - Org/role context stream

## Models

### LoginCredentials

```typescript
interface LoginCredentials {
  email: string;
  password: string;
  baseUrl: string;
  state?: string;
}
```

### OAuthLoginParams

```typescript
interface OAuthLoginParams {
  token: OAuthToken;
  loginType: 'oauth' | 'saml';
  baseUrl: string;
}

interface OAuthToken {
  accessToken: string;
  expireAt: number;
  refreshToken: string;
}
```

### OrgRoleContext

```typescript
interface OrgRoleContext {
  organizationId: string;
  organizationName: string;
  roleId: string;
  roleName: string;
  baseUrl: string;
}
```

### AuthenticationState

```typescript
interface AuthenticationState {
  isAuthenticated: boolean;
  isOrgRoleSet: boolean;
  loginType: 'local' | 'oauth' | 'saml' | null;
  isLoading: boolean;
  error: string | null;
}
```

## Testing

```bash
# Run unit tests
yarn test

# Run tests in watch mode
yarn test:watch

# Generate coverage report
yarn test:coverage
```

## License

This project is licensed under the MIT License.