@medha-analytics/medhasso-auth 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +296 -296
- package/fesm2022/medha-analytics-medhasso-auth.mjs +48 -23
- package/fesm2022/medha-analytics-medhasso-auth.mjs.map +1 -1
- package/lib/interceptors/medhasso-auth.interceptor.d.ts +10 -1
- package/lib/interfaces/medhasso-config.interface.d.ts +10 -4
- package/lib/services/token-management.service.d.ts +20 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,297 +1,297 @@
|
|
|
1
|
-
# MedHasso Authentication Library
|
|
2
|
-
|
|
3
|
-
Angular authentication library for seamless integration with MedHasso SSO services. This library provides JWT token management, route guards, HTTP interceptors, and user management utilities.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- 🔐 **JWT Token Management** - Automatic token refresh and storage
|
|
8
|
-
- 🛡️ **Route Guards** - Protect routes with authentication and role-based access
|
|
9
|
-
- 🔄 **HTTP Interceptor** - Automatic token attachment to HTTP requests
|
|
10
|
-
- 👤 **User Management** - Extract and manage user information from JWT tokens
|
|
11
|
-
- ⚙️ **Configurable** - Extensive configuration options for different environments
|
|
12
|
-
- 📱 **Angular 14+ Support** - Compatible with Angular 14 through latest versions
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
npm install @medha-analytics/medhasso-auth
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Quick Start
|
|
21
|
-
|
|
22
|
-
### 1. Import and Configure
|
|
23
|
-
|
|
24
|
-
In your `app.config.ts` (Angular 17+ standalone) or `app.module.ts`:
|
|
25
|
-
|
|
26
|
-
#### Standalone Bootstrap (Angular 17+)
|
|
27
|
-
```typescript
|
|
28
|
-
import { bootstrapApplication } from '@angular/platform-browser';
|
|
29
|
-
import { provideRouter } from '@angular/router';
|
|
30
|
-
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
|
|
31
|
-
import { MedHassoAuthModule } from '@medha-analytics/medhasso-auth';
|
|
32
|
-
import { AppComponent } from './app/app.component';
|
|
33
|
-
import { routes } from './app/app.routes';
|
|
34
|
-
import { environment } from './environments/environment';
|
|
35
|
-
|
|
36
|
-
bootstrapApplication(AppComponent, {
|
|
37
|
-
providers: [
|
|
38
|
-
provideRouter(routes),
|
|
39
|
-
provideHttpClient(withInterceptorsFromDi()),
|
|
40
|
-
...MedHassoAuthModule.forStandalone({
|
|
41
|
-
ssoUrl: environment.ssoUrl,
|
|
42
|
-
refreshUrl: environment.refreshUrl,
|
|
43
|
-
applicationName: 'Your App Name'
|
|
44
|
-
})
|
|
45
|
-
]
|
|
46
|
-
});
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
#### Module-based (Angular 14-16)
|
|
50
|
-
```typescript
|
|
51
|
-
import { NgModule } from '@angular/core';
|
|
52
|
-
import { BrowserModule } from '@angular/platform-browser';
|
|
53
|
-
import { HttpClientModule } from '@angular/common/http';
|
|
54
|
-
import { MedHassoAuthModule } from '@medha-analytics/medhasso-auth';
|
|
55
|
-
import { AppComponent } from './app.component';
|
|
56
|
-
import { environment } from '../environments/environment';
|
|
57
|
-
|
|
58
|
-
@NgModule({
|
|
59
|
-
declarations: [AppComponent],
|
|
60
|
-
imports: [
|
|
61
|
-
BrowserModule,
|
|
62
|
-
HttpClientModule,
|
|
63
|
-
MedHassoAuthModule.forRoot({
|
|
64
|
-
ssoUrl: environment.ssoUrl,
|
|
65
|
-
refreshUrl: environment.refreshUrl,
|
|
66
|
-
applicationName: 'Your App Name'
|
|
67
|
-
})
|
|
68
|
-
],
|
|
69
|
-
bootstrap: [AppComponent]
|
|
70
|
-
})
|
|
71
|
-
export class AppModule { }
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### 2. Protect Routes
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
import { Routes } from '@angular/router';
|
|
78
|
-
import { MedHassoAuthGuard, MedHassoRoleGuard, MedHassoAdminGuard } from '@medha-analytics/medhasso-auth';
|
|
79
|
-
|
|
80
|
-
export const routes: Routes = [
|
|
81
|
-
{
|
|
82
|
-
path: 'dashboard',
|
|
83
|
-
component: DashboardComponent,
|
|
84
|
-
canActivate: [MedHassoAuthGuard]
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
path: 'admin',
|
|
88
|
-
component: AdminComponent,
|
|
89
|
-
canActivate: [MedHassoAdminGuard]
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
path: 'manager',
|
|
93
|
-
component: ManagerComponent,
|
|
94
|
-
canActivate: [MedHassoRoleGuard],
|
|
95
|
-
data: { roles: ['manager', 'admin'] }
|
|
96
|
-
}
|
|
97
|
-
];
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### 3. Use in Components
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
import { Component } from '@angular/core';
|
|
104
|
-
import { MedHassoAuthService } from '@medha-analytics/medhasso-auth';
|
|
105
|
-
|
|
106
|
-
@Component({
|
|
107
|
-
selector: 'app-user-profile',
|
|
108
|
-
template: `
|
|
109
|
-
<div *ngIf="authService.isAuthenticated()">
|
|
110
|
-
<h2>Welcome, {{ authService.getUserDisplayName() }}!</h2>
|
|
111
|
-
<p>Email: {{ authService.getUserEmail() }}</p>
|
|
112
|
-
<div *ngIf="authService.isAdmin()">
|
|
113
|
-
<button>Admin Panel</button>
|
|
114
|
-
</div>
|
|
115
|
-
<button (click)="logout()">Logout</button>
|
|
116
|
-
</div>
|
|
117
|
-
`
|
|
118
|
-
})
|
|
119
|
-
export class UserProfileComponent {
|
|
120
|
-
constructor(public authService: MedHassoAuthService) {}
|
|
121
|
-
|
|
122
|
-
logout() {
|
|
123
|
-
this.authService.logout();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
## Configuration Options
|
|
129
|
-
|
|
130
|
-
### Environment Setup
|
|
131
|
-
First, configure your environment files:
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
// src/environments/environment.ts
|
|
135
|
-
export const environment = {
|
|
136
|
-
production: false,
|
|
137
|
-
ssoUrl: 'https://your-dev-sso.domain.com:3000',
|
|
138
|
-
refreshUrl: 'https://your-dev-refresh.domain.com:4000',
|
|
139
|
-
// ... other config
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
// src/environments/environment.production.ts
|
|
143
|
-
export const environment = {
|
|
144
|
-
production: true,
|
|
145
|
-
ssoUrl: 'https://your-prod-sso.domain.com:3000',
|
|
146
|
-
refreshUrl: 'https://your-prod-refresh.domain.com:4000',
|
|
147
|
-
// ... other config
|
|
148
|
-
};
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Basic Configuration
|
|
152
|
-
```typescript
|
|
153
|
-
{
|
|
154
|
-
ssoUrl: environment.ssoUrl,
|
|
155
|
-
refreshUrl: environment.refreshUrl,
|
|
156
|
-
applicationName: 'Your Application Name'
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Advanced Configuration
|
|
161
|
-
```typescript
|
|
162
|
-
{
|
|
163
|
-
ssoUrl: environment.ssoUrl,
|
|
164
|
-
refreshUrl: environment.refreshUrl,
|
|
165
|
-
applicationName: 'Your Application Name',
|
|
166
|
-
|
|
167
|
-
// Token configuration
|
|
168
|
-
tokenConfig: {
|
|
169
|
-
storageType: 'localStorage', // or 'sessionStorage'
|
|
170
|
-
idTokenKey: 'idToken',
|
|
171
|
-
refreshTokenKey: 'refreshToken',
|
|
172
|
-
refreshBeforeExpiryMinutes: 5,
|
|
173
|
-
autoRefresh: true
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
// HTTP Interceptor configuration
|
|
177
|
-
interceptorConfig: {
|
|
178
|
-
enabled: true,
|
|
179
|
-
authHeaderPrefix: 'Bearer',
|
|
180
|
-
excludeUrls: ['*/public/*', '*/assets/*'],
|
|
181
|
-
includeUrls: ['*/api/*'] // Optional: if specified, only these URLs get tokens
|
|
182
|
-
},
|
|
183
|
-
|
|
184
|
-
// Security configuration
|
|
185
|
-
securityConfig: {
|
|
186
|
-
httpsOnly: true,
|
|
187
|
-
secureCookies: true,
|
|
188
|
-
additionalCspSources: ['https://your-cdn.com']
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
## Guards
|
|
194
|
-
|
|
195
|
-
### MedHassoAuthGuard
|
|
196
|
-
Basic authentication guard that ensures user is logged in.
|
|
197
|
-
|
|
198
|
-
### MedHassoRoleGuard
|
|
199
|
-
Role-based access guard. Use with route data:
|
|
200
|
-
```typescript
|
|
201
|
-
{
|
|
202
|
-
path: 'admin',
|
|
203
|
-
component: AdminComponent,
|
|
204
|
-
canActivate: [MedHassoRoleGuard],
|
|
205
|
-
data: {
|
|
206
|
-
role: 'admin', // Single role
|
|
207
|
-
roles: ['admin', 'manager'] // Multiple roles (OR condition)
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### MedHassoAdminGuard
|
|
213
|
-
Admin-only access guard.
|
|
214
|
-
|
|
215
|
-
## Services
|
|
216
|
-
|
|
217
|
-
### MedHassoAuthService
|
|
218
|
-
Main authentication service with methods:
|
|
219
|
-
- `isAuthenticated(): boolean`
|
|
220
|
-
- `getCurrentUser(): MedHassoUser | null`
|
|
221
|
-
- `hasRole(role: string): boolean`
|
|
222
|
-
- `isAdmin(): boolean`
|
|
223
|
-
- `login(returnUrl?: string): void`
|
|
224
|
-
- `logout(): void`
|
|
225
|
-
- `getUserEmail(): string | null`
|
|
226
|
-
- `getUserDisplayName(): string | null`
|
|
227
|
-
|
|
228
|
-
### TokenManagementService
|
|
229
|
-
Low-level token management (usually not needed directly):
|
|
230
|
-
- `getTokenInfo(): TokenInfo | null`
|
|
231
|
-
- `refreshToken(): Observable<any>`
|
|
232
|
-
- `setTokens(idToken: string, refreshToken?: string): void`
|
|
233
|
-
|
|
234
|
-
## Environment-Specific Setup
|
|
235
|
-
|
|
236
|
-
### Development
|
|
237
|
-
```typescript
|
|
238
|
-
// src/environments/environment.ts
|
|
239
|
-
export const environment = {
|
|
240
|
-
production: false,
|
|
241
|
-
ssoUrl: 'https://dev-sso.your-domain.com:3000',
|
|
242
|
-
refreshUrl: 'https://dev-refresh.your-domain.com:4000'
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
// Usage in module/config
|
|
246
|
-
MedHassoAuthModule.forRoot({
|
|
247
|
-
ssoUrl: environment.ssoUrl,
|
|
248
|
-
refreshUrl: environment.refreshUrl,
|
|
249
|
-
applicationName: 'Your App (Dev)'
|
|
250
|
-
})
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### Production
|
|
254
|
-
```typescript
|
|
255
|
-
// src/environments/environment.production.ts
|
|
256
|
-
export const environment = {
|
|
257
|
-
production: true,
|
|
258
|
-
ssoUrl: 'https://sso.your-domain.com:3000',
|
|
259
|
-
refreshUrl: 'https://refresh.your-domain.com:4000'
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
// Usage in module/config
|
|
263
|
-
MedHassoAuthModule.forRoot({
|
|
264
|
-
ssoUrl: environment.ssoUrl,
|
|
265
|
-
refreshUrl: environment.refreshUrl,
|
|
266
|
-
applicationName: 'Your App'
|
|
267
|
-
})
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
## CSP Configuration
|
|
271
|
-
|
|
272
|
-
For server-side Content Security Policy, include these domains:
|
|
273
|
-
|
|
274
|
-
```javascript
|
|
275
|
-
// In your server configuration (e.g., Express.js)
|
|
276
|
-
const cspSources = [
|
|
277
|
-
process.env.SSO_URL || 'https://your-sso.domain.com:3000',
|
|
278
|
-
process.env.REFRESH_URL || 'https://your-refresh.domain.com:4000'
|
|
279
|
-
];
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
## Migration Guide
|
|
283
|
-
|
|
284
|
-
### From Custom Implementation
|
|
285
|
-
1. Remove your existing auth guard, interceptor, and token service
|
|
286
|
-
2. Install this library
|
|
287
|
-
3. Update your imports and module configuration
|
|
288
|
-
4. Update route guards to use library guards
|
|
289
|
-
5. Update components to use `MedHassoAuthService`
|
|
290
|
-
|
|
291
|
-
## Contributing
|
|
292
|
-
|
|
293
|
-
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
294
|
-
|
|
295
|
-
## License
|
|
296
|
-
|
|
1
|
+
# MedHasso Authentication Library
|
|
2
|
+
|
|
3
|
+
Angular authentication library for seamless integration with MedHasso SSO services. This library provides JWT token management, route guards, HTTP interceptors, and user management utilities.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔐 **JWT Token Management** - Automatic token refresh and storage
|
|
8
|
+
- 🛡️ **Route Guards** - Protect routes with authentication and role-based access
|
|
9
|
+
- 🔄 **HTTP Interceptor** - Automatic token attachment to HTTP requests
|
|
10
|
+
- 👤 **User Management** - Extract and manage user information from JWT tokens
|
|
11
|
+
- ⚙️ **Configurable** - Extensive configuration options for different environments
|
|
12
|
+
- 📱 **Angular 14+ Support** - Compatible with Angular 14 through latest versions
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @medha-analytics/medhasso-auth
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### 1. Import and Configure
|
|
23
|
+
|
|
24
|
+
In your `app.config.ts` (Angular 17+ standalone) or `app.module.ts`:
|
|
25
|
+
|
|
26
|
+
#### Standalone Bootstrap (Angular 17+)
|
|
27
|
+
```typescript
|
|
28
|
+
import { bootstrapApplication } from '@angular/platform-browser';
|
|
29
|
+
import { provideRouter } from '@angular/router';
|
|
30
|
+
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
|
|
31
|
+
import { MedHassoAuthModule } from '@medha-analytics/medhasso-auth';
|
|
32
|
+
import { AppComponent } from './app/app.component';
|
|
33
|
+
import { routes } from './app/app.routes';
|
|
34
|
+
import { environment } from './environments/environment';
|
|
35
|
+
|
|
36
|
+
bootstrapApplication(AppComponent, {
|
|
37
|
+
providers: [
|
|
38
|
+
provideRouter(routes),
|
|
39
|
+
provideHttpClient(withInterceptorsFromDi()),
|
|
40
|
+
...MedHassoAuthModule.forStandalone({
|
|
41
|
+
ssoUrl: environment.ssoUrl,
|
|
42
|
+
refreshUrl: environment.refreshUrl,
|
|
43
|
+
applicationName: 'Your App Name'
|
|
44
|
+
})
|
|
45
|
+
]
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
#### Module-based (Angular 14-16)
|
|
50
|
+
```typescript
|
|
51
|
+
import { NgModule } from '@angular/core';
|
|
52
|
+
import { BrowserModule } from '@angular/platform-browser';
|
|
53
|
+
import { HttpClientModule } from '@angular/common/http';
|
|
54
|
+
import { MedHassoAuthModule } from '@medha-analytics/medhasso-auth';
|
|
55
|
+
import { AppComponent } from './app.component';
|
|
56
|
+
import { environment } from '../environments/environment';
|
|
57
|
+
|
|
58
|
+
@NgModule({
|
|
59
|
+
declarations: [AppComponent],
|
|
60
|
+
imports: [
|
|
61
|
+
BrowserModule,
|
|
62
|
+
HttpClientModule,
|
|
63
|
+
MedHassoAuthModule.forRoot({
|
|
64
|
+
ssoUrl: environment.ssoUrl,
|
|
65
|
+
refreshUrl: environment.refreshUrl,
|
|
66
|
+
applicationName: 'Your App Name'
|
|
67
|
+
})
|
|
68
|
+
],
|
|
69
|
+
bootstrap: [AppComponent]
|
|
70
|
+
})
|
|
71
|
+
export class AppModule { }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 2. Protect Routes
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { Routes } from '@angular/router';
|
|
78
|
+
import { MedHassoAuthGuard, MedHassoRoleGuard, MedHassoAdminGuard } from '@medha-analytics/medhasso-auth';
|
|
79
|
+
|
|
80
|
+
export const routes: Routes = [
|
|
81
|
+
{
|
|
82
|
+
path: 'dashboard',
|
|
83
|
+
component: DashboardComponent,
|
|
84
|
+
canActivate: [MedHassoAuthGuard]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
path: 'admin',
|
|
88
|
+
component: AdminComponent,
|
|
89
|
+
canActivate: [MedHassoAdminGuard]
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
path: 'manager',
|
|
93
|
+
component: ManagerComponent,
|
|
94
|
+
canActivate: [MedHassoRoleGuard],
|
|
95
|
+
data: { roles: ['manager', 'admin'] }
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 3. Use in Components
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { Component } from '@angular/core';
|
|
104
|
+
import { MedHassoAuthService } from '@medha-analytics/medhasso-auth';
|
|
105
|
+
|
|
106
|
+
@Component({
|
|
107
|
+
selector: 'app-user-profile',
|
|
108
|
+
template: `
|
|
109
|
+
<div *ngIf="authService.isAuthenticated()">
|
|
110
|
+
<h2>Welcome, {{ authService.getUserDisplayName() }}!</h2>
|
|
111
|
+
<p>Email: {{ authService.getUserEmail() }}</p>
|
|
112
|
+
<div *ngIf="authService.isAdmin()">
|
|
113
|
+
<button>Admin Panel</button>
|
|
114
|
+
</div>
|
|
115
|
+
<button (click)="logout()">Logout</button>
|
|
116
|
+
</div>
|
|
117
|
+
`
|
|
118
|
+
})
|
|
119
|
+
export class UserProfileComponent {
|
|
120
|
+
constructor(public authService: MedHassoAuthService) {}
|
|
121
|
+
|
|
122
|
+
logout() {
|
|
123
|
+
this.authService.logout();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Configuration Options
|
|
129
|
+
|
|
130
|
+
### Environment Setup
|
|
131
|
+
First, configure your environment files:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// src/environments/environment.ts
|
|
135
|
+
export const environment = {
|
|
136
|
+
production: false,
|
|
137
|
+
ssoUrl: 'https://your-dev-sso.domain.com:3000',
|
|
138
|
+
refreshUrl: 'https://your-dev-refresh.domain.com:4000',
|
|
139
|
+
// ... other config
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/environments/environment.production.ts
|
|
143
|
+
export const environment = {
|
|
144
|
+
production: true,
|
|
145
|
+
ssoUrl: 'https://your-prod-sso.domain.com:3000',
|
|
146
|
+
refreshUrl: 'https://your-prod-refresh.domain.com:4000',
|
|
147
|
+
// ... other config
|
|
148
|
+
};
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Basic Configuration
|
|
152
|
+
```typescript
|
|
153
|
+
{
|
|
154
|
+
ssoUrl: environment.ssoUrl,
|
|
155
|
+
refreshUrl: environment.refreshUrl,
|
|
156
|
+
applicationName: 'Your Application Name'
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Advanced Configuration
|
|
161
|
+
```typescript
|
|
162
|
+
{
|
|
163
|
+
ssoUrl: environment.ssoUrl,
|
|
164
|
+
refreshUrl: environment.refreshUrl,
|
|
165
|
+
applicationName: 'Your Application Name',
|
|
166
|
+
|
|
167
|
+
// Token configuration
|
|
168
|
+
tokenConfig: {
|
|
169
|
+
storageType: 'localStorage', // or 'sessionStorage'
|
|
170
|
+
idTokenKey: 'idToken',
|
|
171
|
+
refreshTokenKey: 'refreshToken',
|
|
172
|
+
refreshBeforeExpiryMinutes: 5,
|
|
173
|
+
autoRefresh: true
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
// HTTP Interceptor configuration
|
|
177
|
+
interceptorConfig: {
|
|
178
|
+
enabled: true,
|
|
179
|
+
authHeaderPrefix: 'Bearer',
|
|
180
|
+
excludeUrls: ['*/public/*', '*/assets/*'],
|
|
181
|
+
includeUrls: ['*/api/*'] // Optional: if specified, only these URLs get tokens
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
// Security configuration
|
|
185
|
+
securityConfig: {
|
|
186
|
+
httpsOnly: true,
|
|
187
|
+
secureCookies: true,
|
|
188
|
+
additionalCspSources: ['https://your-cdn.com']
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Guards
|
|
194
|
+
|
|
195
|
+
### MedHassoAuthGuard
|
|
196
|
+
Basic authentication guard that ensures user is logged in.
|
|
197
|
+
|
|
198
|
+
### MedHassoRoleGuard
|
|
199
|
+
Role-based access guard. Use with route data:
|
|
200
|
+
```typescript
|
|
201
|
+
{
|
|
202
|
+
path: 'admin',
|
|
203
|
+
component: AdminComponent,
|
|
204
|
+
canActivate: [MedHassoRoleGuard],
|
|
205
|
+
data: {
|
|
206
|
+
role: 'admin', // Single role
|
|
207
|
+
roles: ['admin', 'manager'] // Multiple roles (OR condition)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### MedHassoAdminGuard
|
|
213
|
+
Admin-only access guard.
|
|
214
|
+
|
|
215
|
+
## Services
|
|
216
|
+
|
|
217
|
+
### MedHassoAuthService
|
|
218
|
+
Main authentication service with methods:
|
|
219
|
+
- `isAuthenticated(): boolean`
|
|
220
|
+
- `getCurrentUser(): MedHassoUser | null`
|
|
221
|
+
- `hasRole(role: string): boolean`
|
|
222
|
+
- `isAdmin(): boolean`
|
|
223
|
+
- `login(returnUrl?: string): void`
|
|
224
|
+
- `logout(): void`
|
|
225
|
+
- `getUserEmail(): string | null`
|
|
226
|
+
- `getUserDisplayName(): string | null`
|
|
227
|
+
|
|
228
|
+
### TokenManagementService
|
|
229
|
+
Low-level token management (usually not needed directly):
|
|
230
|
+
- `getTokenInfo(): TokenInfo | null`
|
|
231
|
+
- `refreshToken(): Observable<any>`
|
|
232
|
+
- `setTokens(idToken: string, refreshToken?: string): void`
|
|
233
|
+
|
|
234
|
+
## Environment-Specific Setup
|
|
235
|
+
|
|
236
|
+
### Development
|
|
237
|
+
```typescript
|
|
238
|
+
// src/environments/environment.ts
|
|
239
|
+
export const environment = {
|
|
240
|
+
production: false,
|
|
241
|
+
ssoUrl: 'https://dev-sso.your-domain.com:3000',
|
|
242
|
+
refreshUrl: 'https://dev-refresh.your-domain.com:4000'
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Usage in module/config
|
|
246
|
+
MedHassoAuthModule.forRoot({
|
|
247
|
+
ssoUrl: environment.ssoUrl,
|
|
248
|
+
refreshUrl: environment.refreshUrl,
|
|
249
|
+
applicationName: 'Your App (Dev)'
|
|
250
|
+
})
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Production
|
|
254
|
+
```typescript
|
|
255
|
+
// src/environments/environment.production.ts
|
|
256
|
+
export const environment = {
|
|
257
|
+
production: true,
|
|
258
|
+
ssoUrl: 'https://sso.your-domain.com:3000',
|
|
259
|
+
refreshUrl: 'https://refresh.your-domain.com:4000'
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Usage in module/config
|
|
263
|
+
MedHassoAuthModule.forRoot({
|
|
264
|
+
ssoUrl: environment.ssoUrl,
|
|
265
|
+
refreshUrl: environment.refreshUrl,
|
|
266
|
+
applicationName: 'Your App'
|
|
267
|
+
})
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## CSP Configuration
|
|
271
|
+
|
|
272
|
+
For server-side Content Security Policy, include these domains:
|
|
273
|
+
|
|
274
|
+
```javascript
|
|
275
|
+
// In your server configuration (e.g., Express.js)
|
|
276
|
+
const cspSources = [
|
|
277
|
+
process.env.SSO_URL || 'https://your-sso.domain.com:3000',
|
|
278
|
+
process.env.REFRESH_URL || 'https://your-refresh.domain.com:4000'
|
|
279
|
+
];
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Migration Guide
|
|
283
|
+
|
|
284
|
+
### From Custom Implementation
|
|
285
|
+
1. Remove your existing auth guard, interceptor, and token service
|
|
286
|
+
2. Install this library
|
|
287
|
+
3. Update your imports and module configuration
|
|
288
|
+
4. Update route guards to use library guards
|
|
289
|
+
5. Update components to use `MedHassoAuthService`
|
|
290
|
+
|
|
291
|
+
## Contributing
|
|
292
|
+
|
|
293
|
+
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
294
|
+
|
|
295
|
+
## License
|
|
296
|
+
|
|
297
297
|
MIT License - see [LICENSE](LICENSE) file for details.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, Inject, Injectable, NgModule } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common/http';
|
|
4
|
-
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
4
|
+
import { HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http';
|
|
5
5
|
import { BehaviorSubject, timer, throwError, of } from 'rxjs';
|
|
6
6
|
import { tap, catchError, finalize, shareReplay, map, switchMap } from 'rxjs/operators';
|
|
7
7
|
import * as i2 from '@angular/router';
|
|
@@ -27,7 +27,7 @@ class TokenManagementService {
|
|
|
27
27
|
get autoRefresh() {
|
|
28
28
|
return this.tokenConfig.autoRefresh !== false;
|
|
29
29
|
}
|
|
30
|
-
constructor(config, http) {
|
|
30
|
+
constructor(config, http, httpBackend) {
|
|
31
31
|
this.config = config;
|
|
32
32
|
this.http = http;
|
|
33
33
|
this.refreshInProgress$ = null;
|
|
@@ -43,6 +43,7 @@ class TokenManagementService {
|
|
|
43
43
|
};
|
|
44
44
|
this.token$ = this.tokenSubject.asObservable();
|
|
45
45
|
this.user$ = this.userSubject.asObservable();
|
|
46
|
+
this.refreshHttp = new HttpClient(httpBackend);
|
|
46
47
|
this.initializeFromStorage();
|
|
47
48
|
this.setupWakeDetection();
|
|
48
49
|
}
|
|
@@ -140,19 +141,26 @@ class TokenManagementService {
|
|
|
140
141
|
storage.removeItem(this.refreshTokenKey);
|
|
141
142
|
}
|
|
142
143
|
/**
|
|
143
|
-
* Create token info object from JWT token
|
|
144
|
+
* Create token info object from JWT token.
|
|
145
|
+
*
|
|
146
|
+
* `isExpired` and `needsRefresh` are getters, not snapshotted booleans:
|
|
147
|
+
* the TokenInfo object may outlive wall-clock events that change its
|
|
148
|
+
* expiry status (e.g. OS/browser suspended overnight, laptop sleep).
|
|
149
|
+
* A snapshotted boolean captured at creation time would then lie — the
|
|
150
|
+
* wake handler would call ensureValidToken(), see `isExpired: false`
|
|
151
|
+
* from the pre-sleep snapshot, and skip the refresh. Reading these as
|
|
152
|
+
* getters means every access reflects the current clock.
|
|
144
153
|
*/
|
|
145
154
|
createTokenInfo(token) {
|
|
146
155
|
const payload = this.decodeJwt(token);
|
|
147
156
|
const expiresAt = payload.exp * 1000; // Convert to milliseconds
|
|
148
|
-
const now = Date.now();
|
|
149
157
|
const refreshWindow = this.refreshBeforeExpiryMinutes * 60 * 1000;
|
|
150
158
|
return {
|
|
151
159
|
token,
|
|
152
160
|
payload,
|
|
153
161
|
expiresAt,
|
|
154
|
-
isExpired
|
|
155
|
-
needsRefresh
|
|
162
|
+
get isExpired() { return Date.now() >= expiresAt; },
|
|
163
|
+
get needsRefresh() { return Date.now() >= (expiresAt - refreshWindow); }
|
|
156
164
|
};
|
|
157
165
|
}
|
|
158
166
|
/**
|
|
@@ -272,14 +280,24 @@ class TokenManagementService {
|
|
|
272
280
|
if (!refreshToken) {
|
|
273
281
|
return throwError(() => new Error('No refresh token available'));
|
|
274
282
|
}
|
|
275
|
-
const request$ = this.
|
|
283
|
+
const request$ = this.refreshHttp.post(`${this.config.refreshUrl}/api/refresh-token`, { refreshToken })
|
|
276
284
|
.pipe(tap((response) => {
|
|
277
285
|
if (response.id_token) {
|
|
278
286
|
this.setTokens(response.id_token, response.refresh_token);
|
|
279
287
|
}
|
|
280
288
|
}), catchError(error => {
|
|
281
289
|
console.error('Token refresh failed:', error);
|
|
282
|
-
|
|
290
|
+
// Only clear the session on a definitive auth failure from the refresh
|
|
291
|
+
// endpoint (dead/revoked/invalid refresh token). Transient failures —
|
|
292
|
+
// network blips, 5xx, timeouts — must NOT force a logout: the current
|
|
293
|
+
// id_token may still be valid for the rest of its lifetime, and the
|
|
294
|
+
// background-refresh path (ensureValidToken → needsRefresh) runs on
|
|
295
|
+
// every request within the 5-min window, so a single network hiccup
|
|
296
|
+
// would otherwise log the user out mid-session.
|
|
297
|
+
const status = error?.status;
|
|
298
|
+
if (status === 400 || status === 401) {
|
|
299
|
+
this.logout();
|
|
300
|
+
}
|
|
283
301
|
return throwError(() => error);
|
|
284
302
|
}), finalize(() => {
|
|
285
303
|
this.refreshInProgress$ = null;
|
|
@@ -370,7 +388,7 @@ class TokenManagementService {
|
|
|
370
388
|
this.clearTokenRefreshInterval();
|
|
371
389
|
this.teardownWakeDetection();
|
|
372
390
|
}
|
|
373
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TokenManagementService, deps: [{ token: MEDHASSO_CONFIG }, { token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
391
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TokenManagementService, deps: [{ token: MEDHASSO_CONFIG }, { token: i1.HttpClient }, { token: i1.HttpBackend }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
374
392
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TokenManagementService, providedIn: 'root' }); }
|
|
375
393
|
}
|
|
376
394
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TokenManagementService, decorators: [{
|
|
@@ -381,7 +399,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
381
399
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
382
400
|
type: Inject,
|
|
383
401
|
args: [MEDHASSO_CONFIG]
|
|
384
|
-
}] }, { type: i1.HttpClient }] });
|
|
402
|
+
}] }, { type: i1.HttpClient }, { type: i1.HttpBackend }] });
|
|
385
403
|
|
|
386
404
|
class MedHassoAuthInterceptor {
|
|
387
405
|
constructor(config, tokenService) {
|
|
@@ -470,25 +488,32 @@ class MedHassoAuthInterceptor {
|
|
|
470
488
|
});
|
|
471
489
|
}
|
|
472
490
|
/**
|
|
473
|
-
* Handle 401 Unauthorized errors by attempting token refresh
|
|
491
|
+
* Handle 401 Unauthorized errors by attempting token refresh.
|
|
492
|
+
*
|
|
493
|
+
* Always tries the refresh path first, even when the in-memory token looks
|
|
494
|
+
* expired to our clock. The server already said "unauthorized" — whether our
|
|
495
|
+
* local isExpired check agrees doesn't change what we need to do next.
|
|
496
|
+
* Short-circuiting to SSO on `tokenInfo.isExpired` would bounce the user on
|
|
497
|
+
* mid-flight expirations and small client/server clock skews, cases where the
|
|
498
|
+
* refresh token is still perfectly valid and would have recovered. If the
|
|
499
|
+
* refresh really can't succeed (no refresh token in storage, dead refresh
|
|
500
|
+
* token → 400/401), refreshToken() throws and the catchError below redirects.
|
|
474
501
|
*/
|
|
475
502
|
handle401Error(request, next) {
|
|
476
|
-
|
|
477
|
-
//
|
|
478
|
-
|
|
503
|
+
// catchError sits BEFORE switchMap on purpose: it must only catch failures
|
|
504
|
+
// from refreshToken(), not from the retried next.handle(retryRequest). If
|
|
505
|
+
// the refresh succeeds but the retry fails (403, 500, etc.), that error
|
|
506
|
+
// has nothing to do with authentication — redirecting to SSO would be
|
|
507
|
+
// wrong. Keep the SSO redirect scoped to the "refresh really can't help"
|
|
508
|
+
// case; let any retry error propagate normally to the component.
|
|
509
|
+
return this.tokenService.refreshToken().pipe(catchError((refreshError) => {
|
|
510
|
+
console.error('MedHasso Auth: Token refresh failed', refreshError);
|
|
479
511
|
this.redirectToSSO();
|
|
480
|
-
return throwError(() =>
|
|
481
|
-
}
|
|
482
|
-
// If token exists but request failed, try to refresh
|
|
483
|
-
return this.tokenService.refreshToken().pipe(switchMap(() => {
|
|
512
|
+
return throwError(() => refreshError);
|
|
513
|
+
}), switchMap(() => {
|
|
484
514
|
// Retry the original request with new token
|
|
485
515
|
const retryRequest = this.addAuthorizationHeader(request);
|
|
486
516
|
return next.handle(retryRequest);
|
|
487
|
-
}), catchError((refreshError) => {
|
|
488
|
-
// If refresh fails, redirect to SSO
|
|
489
|
-
console.error('MedHasso Auth: Token refresh failed', refreshError);
|
|
490
|
-
this.redirectToSSO();
|
|
491
|
-
return throwError(() => refreshError);
|
|
492
517
|
}));
|
|
493
518
|
}
|
|
494
519
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"medha-analytics-medhasso-auth.mjs","sources":["../../../projects/medhasso-auth/src/lib/tokens/medhasso-config.token.ts","../../../projects/medhasso-auth/src/lib/services/token-management.service.ts","../../../projects/medhasso-auth/src/lib/interceptors/medhasso-auth.interceptor.ts","../../../projects/medhasso-auth/src/lib/services/medhasso-auth.service.ts","../../../projects/medhasso-auth/src/lib/medhasso-auth.module.ts","../../../projects/medhasso-auth/src/lib/guards/medhasso-auth.guard.ts","../../../projects/medhasso-auth/src/public-api.ts","../../../projects/medhasso-auth/src/medha-analytics-medhasso-auth.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\r\nimport { MedHassoConfig } from '../interfaces/medhasso-config.interface';\r\n\r\nexport const MEDHASSO_CONFIG = new InjectionToken<MedHassoConfig>('MEDHASSO_CONFIG');","import { Injectable, Inject } from '@angular/core';\r\nimport { HttpClient } from '@angular/common/http';\r\nimport { BehaviorSubject, Observable, of, throwError, timer } from 'rxjs';\r\nimport { catchError, finalize, map, shareReplay, tap } from 'rxjs/operators';\r\nimport { MedHassoConfig, TokenConfig, TokenInfo, MedHassoUser } from '../interfaces/medhasso-config.interface';\r\nimport { MEDHASSO_CONFIG } from '../tokens/medhasso-config.token';\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class TokenManagementService {\r\n private refreshTokenInterval: any;\r\n private refreshInProgress$: Observable<any> | null = null;\r\n private tokenSubject = new BehaviorSubject<TokenInfo | null>(null);\r\n private userSubject = new BehaviorSubject<MedHassoUser | null>(null);\r\n private readonly onVisibilityChange = () => {\r\n if (document.visibilityState === 'visible') {\r\n this.ensureValidToken().subscribe();\r\n }\r\n };\r\n private readonly onWindowFocus = () => {\r\n this.ensureValidToken().subscribe();\r\n };\r\n\r\n public token$ = this.tokenSubject.asObservable();\r\n public user$ = this.userSubject.asObservable();\r\n\r\n private get tokenConfig(): TokenConfig {\r\n return this.config.tokenConfig || {};\r\n }\r\n\r\n private get storageType(): 'localStorage' | 'sessionStorage' {\r\n return this.tokenConfig.storageType || 'localStorage';\r\n }\r\n\r\n private get idTokenKey(): string {\r\n return this.tokenConfig.idTokenKey || 'idToken';\r\n }\r\n\r\n private get refreshTokenKey(): string {\r\n return this.tokenConfig.refreshTokenKey || 'refreshToken';\r\n }\r\n\r\n private get refreshBeforeExpiryMinutes(): number {\r\n return this.tokenConfig.refreshBeforeExpiryMinutes || 5;\r\n }\r\n\r\n private get autoRefresh(): boolean {\r\n return this.tokenConfig.autoRefresh !== false;\r\n }\r\n\r\n constructor(\r\n @Inject(MEDHASSO_CONFIG) private config: MedHassoConfig,\r\n private http: HttpClient\r\n ) {\r\n this.initializeFromStorage();\r\n this.setupWakeDetection();\r\n }\r\n\r\n /**\r\n * Re-validate the token whenever the tab regains focus/visibility.\r\n * The refresh timer is a JS timer and does not fire while the OS/browser\r\n * was suspended (e.g. laptop sleep overnight), so it can't be relied on\r\n * alone to catch a token that went stale while the app was inactive.\r\n */\r\n private setupWakeDetection(): void {\r\n if (typeof document === 'undefined' || typeof window === 'undefined') {\r\n return;\r\n }\r\n\r\n document.addEventListener('visibilitychange', this.onVisibilityChange);\r\n window.addEventListener('focus', this.onWindowFocus);\r\n }\r\n\r\n /**\r\n * Remove wake-detection listeners registered in setupWakeDetection().\r\n */\r\n private teardownWakeDetection(): void {\r\n if (typeof document === 'undefined' || typeof window === 'undefined') {\r\n return;\r\n }\r\n\r\n document.removeEventListener('visibilitychange', this.onVisibilityChange);\r\n window.removeEventListener('focus', this.onWindowFocus);\r\n }\r\n\r\n /**\r\n * Initialize token information from storage\r\n */\r\n private initializeFromStorage(): void {\r\n const token = this.getStoredToken();\r\n if (token) {\r\n const tokenInfo = this.createTokenInfo(token);\r\n this.tokenSubject.next(tokenInfo);\r\n this.userSubject.next(this.extractUserFromToken(token));\r\n \r\n if (this.autoRefresh && !tokenInfo.isExpired) {\r\n this.setTokenRefreshInterval(tokenInfo);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Re-sync in-memory token state from storage if it has drifted (e.g.\r\n * something wrote tokens directly to storage without going through\r\n * setTokens()). No-op when storage already matches memory.\r\n */\r\n private syncFromStorage(): void {\r\n const storedToken = this.getStoredToken();\r\n const currentToken = this.getTokenInfo()?.token ?? null;\r\n\r\n if (storedToken === currentToken) {\r\n return;\r\n }\r\n\r\n if (!storedToken) {\r\n this.tokenSubject.next(null);\r\n this.userSubject.next(null);\r\n this.clearTokenRefreshInterval();\r\n return;\r\n }\r\n\r\n const tokenInfo = this.createTokenInfo(storedToken);\r\n this.tokenSubject.next(tokenInfo);\r\n this.userSubject.next(this.extractUserFromToken(storedToken));\r\n\r\n if (this.autoRefresh && !tokenInfo.isExpired) {\r\n this.setTokenRefreshInterval(tokenInfo);\r\n }\r\n }\r\n\r\n /**\r\n * Get stored token from configured storage\r\n */\r\n private getStoredToken(): string | null {\r\n const storage = this.storageType === 'localStorage' ? localStorage : sessionStorage;\r\n return storage.getItem(this.idTokenKey);\r\n }\r\n\r\n /**\r\n * Get stored refresh token from configured storage\r\n */\r\n private getStoredRefreshToken(): string | null {\r\n const storage = this.storageType === 'localStorage' ? localStorage : sessionStorage;\r\n return storage.getItem(this.refreshTokenKey);\r\n }\r\n\r\n /**\r\n * Store tokens in configured storage\r\n */\r\n private storeTokens(idToken: string, refreshToken?: string): void {\r\n const storage = this.storageType === 'localStorage' ? localStorage : sessionStorage;\r\n storage.setItem(this.idTokenKey, idToken);\r\n if (refreshToken) {\r\n storage.setItem(this.refreshTokenKey, refreshToken);\r\n }\r\n }\r\n\r\n /**\r\n * Clear stored tokens\r\n */\r\n private clearStoredTokens(): void {\r\n const storage = this.storageType === 'localStorage' ? localStorage : sessionStorage;\r\n storage.removeItem(this.idTokenKey);\r\n storage.removeItem(this.refreshTokenKey);\r\n }\r\n\r\n /**\r\n * Create token info object from JWT token\r\n */\r\n private createTokenInfo(token: string): TokenInfo {\r\n const payload = this.decodeJwt(token);\r\n const expiresAt = payload.exp * 1000; // Convert to milliseconds\r\n const now = Date.now();\r\n const refreshWindow = this.refreshBeforeExpiryMinutes * 60 * 1000;\r\n\r\n return {\r\n token,\r\n payload,\r\n expiresAt,\r\n isExpired: now >= expiresAt,\r\n needsRefresh: now >= (expiresAt - refreshWindow)\r\n };\r\n }\r\n\r\n /**\r\n * Decode JWT token payload\r\n */\r\n private decodeJwt(token: string): any {\r\n try {\r\n const parts = token.split('.');\r\n if (parts.length !== 3) {\r\n throw new Error('Invalid JWT token format');\r\n }\r\n const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));\r\n return payload;\r\n } catch (error) {\r\n console.error('Error decoding JWT token:', error);\r\n throw new Error('Invalid JWT token');\r\n }\r\n }\r\n\r\n /**\r\n * Extract user information from JWT token\r\n */\r\n private extractUserFromToken(token: string): MedHassoUser {\r\n const payload = this.decodeJwt(token);\r\n return {\r\n upn: payload.upn || payload.email,\r\n name: payload.name,\r\n roles: payload.roles || [],\r\n ...payload\r\n };\r\n }\r\n\r\n /**\r\n * Set up automatic token refresh interval\r\n */\r\n private setTokenRefreshInterval(tokenInfo: TokenInfo): void {\r\n this.clearTokenRefreshInterval();\r\n\r\n if (!this.autoRefresh || tokenInfo.isExpired) {\r\n return;\r\n }\r\n\r\n const remainingTime = tokenInfo.expiresAt - Date.now();\r\n const refreshTime = remainingTime - (this.refreshBeforeExpiryMinutes * 60 * 1000);\r\n\r\n if (refreshTime > 0) {\r\n this.refreshTokenInterval = timer(refreshTime).subscribe(() => {\r\n this.refreshToken().subscribe();\r\n });\r\n } else {\r\n // Token needs immediate refresh\r\n this.refreshToken().subscribe();\r\n }\r\n }\r\n\r\n /**\r\n * Clear token refresh interval\r\n */\r\n private clearTokenRefreshInterval(): void {\r\n if (this.refreshTokenInterval) {\r\n this.refreshTokenInterval.unsubscribe();\r\n this.refreshTokenInterval = null;\r\n }\r\n }\r\n\r\n /**\r\n * Get current token information\r\n */\r\n getTokenInfo(): TokenInfo | null {\r\n return this.tokenSubject.value;\r\n }\r\n\r\n /**\r\n * Get current user information\r\n */\r\n getCurrentUser(): MedHassoUser | null {\r\n return this.userSubject.value;\r\n }\r\n\r\n /**\r\n * Check if user is authenticated\r\n */\r\n isAuthenticated(): boolean {\r\n const tokenInfo = this.getTokenInfo();\r\n return tokenInfo !== null && !tokenInfo.isExpired;\r\n }\r\n\r\n /**\r\n * Check if user has specific role\r\n */\r\n hasRole(role: string): boolean {\r\n const user = this.getCurrentUser();\r\n return user?.roles?.includes(role) || false;\r\n }\r\n\r\n /**\r\n * Check if user is admin\r\n */\r\n isAdmin(): boolean {\r\n return this.hasRole('admin') || this.hasRole('administrator');\r\n }\r\n\r\n /**\r\n * Set tokens (typically called after successful authentication)\r\n */\r\n setTokens(idToken: string, refreshToken?: string): void {\r\n this.storeTokens(idToken, refreshToken);\r\n const tokenInfo = this.createTokenInfo(idToken);\r\n const user = this.extractUserFromToken(idToken);\r\n\r\n this.tokenSubject.next(tokenInfo);\r\n this.userSubject.next(user);\r\n\r\n if (this.autoRefresh) {\r\n this.setTokenRefreshInterval(tokenInfo);\r\n }\r\n }\r\n\r\n /**\r\n * Refresh the current token.\r\n * Concurrent callers (auto-refresh timer, wake detection, interceptor,\r\n * route guard) share the same in-flight request instead of each firing\r\n * their own POST to the refresh endpoint.\r\n */\r\n refreshToken(): Observable<any> {\r\n if (this.refreshInProgress$) {\r\n return this.refreshInProgress$;\r\n }\r\n\r\n const refreshToken = this.getStoredRefreshToken();\r\n\r\n if (!refreshToken) {\r\n return throwError(() => new Error('No refresh token available'));\r\n }\r\n\r\n const request$ = this.http.post(`${this.config.refreshUrl}/api/refresh-token`, { refreshToken })\r\n .pipe(\r\n tap((response: any) => {\r\n if (response.id_token) {\r\n this.setTokens(response.id_token, response.refresh_token);\r\n }\r\n }),\r\n catchError(error => {\r\n console.error('Token refresh failed:', error);\r\n this.logout();\r\n return throwError(() => error);\r\n }),\r\n finalize(() => {\r\n this.refreshInProgress$ = null;\r\n }),\r\n shareReplay({ bufferSize: 1, refCount: false })\r\n );\r\n\r\n this.refreshInProgress$ = request$;\r\n return request$;\r\n }\r\n\r\n /**\r\n * Ensure the current token is valid, refreshing it first if it is\r\n * expired. Resolves `false` if there is no token at all, or the token\r\n * is expired and refreshing it failed (in both cases the caller should\r\n * redirect to SSO).\r\n *\r\n * A token that merely `needsRefresh` (inside the refresh window, but\r\n * not yet expired) is still good enough to use: the refresh for it is\r\n * kicked off in the background so the *current* request/route isn't\r\n * blocked or logged out by a transient refresh failure (network blip,\r\n * refresh endpoint hiccup) while the existing token would have worked\r\n * fine. Only an actually-expired token blocks the caller.\r\n *\r\n * Re-syncs from storage first: the in-memory token snapshot is only\r\n * updated via setTokens()/logout(), so if something wrote directly to\r\n * storage (e.g. the SSO callback flow) without going through this\r\n * service, the in-memory copy would otherwise be stale until the next\r\n * full page load re-runs initializeFromStorage().\r\n *\r\n * Anonymous/bootstrap endpoints that must be reachable before any token\r\n * exists (e.g. the SSO callback's own token-exchange request) should be\r\n * added to `interceptorConfig.excludeUrls` so they bypass this check\r\n * entirely, rather than relying on this method to let them through.\r\n */\r\n ensureValidToken(): Observable<boolean> {\r\n this.syncFromStorage();\r\n const tokenInfo = this.getTokenInfo();\r\n\r\n if (!tokenInfo) {\r\n return of(false);\r\n }\r\n\r\n if (tokenInfo.isExpired) {\r\n return this.refreshToken().pipe(\r\n map(() => true),\r\n catchError(() => of(false))\r\n );\r\n }\r\n\r\n if (tokenInfo.needsRefresh) {\r\n this.refreshToken().subscribe({ error: () => {} });\r\n }\r\n\r\n return of(true);\r\n }\r\n\r\n /**\r\n * Logout user and clear all tokens\r\n */\r\n logout(): void {\r\n this.clearTokenRefreshInterval();\r\n this.clearStoredTokens();\r\n this.tokenSubject.next(null);\r\n this.userSubject.next(null);\r\n }\r\n\r\n /**\r\n * Redirect to MedHasso SSO for authentication\r\n */\r\n redirectToSSO(returnUrl?: string): void {\r\n const currentUrl = returnUrl || window.location.href;\r\n const ssoUrl = `${this.config.ssoUrl}?url=${encodeURIComponent(currentUrl)}&applicationName=${encodeURIComponent(this.config.applicationName)}`;\r\n window.location.href = ssoUrl;\r\n }\r\n\r\n /**\r\n * Handle authentication callback (typically from SSO redirect)\r\n */\r\n handleAuthCallback(params: { idToken?: string; refreshToken?: string }): boolean {\r\n if (params.idToken) {\r\n this.setTokens(params.idToken, params.refreshToken);\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Get authorization header value\r\n */\r\n getAuthorizationHeader(): string | null {\r\n const tokenInfo = this.getTokenInfo();\r\n if (tokenInfo && !tokenInfo.isExpired) {\r\n const prefix = this.config.interceptorConfig?.authHeaderPrefix || 'Bearer';\r\n return `${prefix} ${tokenInfo.token}`;\r\n }\r\n return null;\r\n }\r\n\r\n /**\r\n * Cleanup on service destruction\r\n */\r\n ngOnDestroy(): void {\r\n this.clearTokenRefreshInterval();\r\n this.teardownWakeDetection();\r\n }\r\n}\r\n","import { Injectable, Inject } from '@angular/core';\r\nimport { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';\r\nimport { Observable, throwError } from 'rxjs';\r\nimport { catchError, switchMap } from 'rxjs/operators';\r\nimport { MedHassoConfig } from '../interfaces/medhasso-config.interface';\r\nimport { MEDHASSO_CONFIG } from '../tokens/medhasso-config.token';\r\nimport { TokenManagementService } from '../services/token-management.service';\r\n\r\n@Injectable()\r\nexport class MedHassoAuthInterceptor implements HttpInterceptor {\r\n\r\n constructor(\r\n @Inject(MEDHASSO_CONFIG) private config: MedHassoConfig,\r\n private tokenService: TokenManagementService\r\n ) {}\r\n\r\n intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {\r\n // Check if interceptor is enabled\r\n if (this.config.interceptorConfig?.enabled === false) {\r\n return next.handle(request);\r\n }\r\n\r\n // Check if request should be excluded\r\n if (this.shouldExcludeRequest(request)) {\r\n return next.handle(request);\r\n }\r\n\r\n // Check if request should be included (if includeUrls is specified)\r\n if (!this.shouldIncludeRequest(request)) {\r\n return next.handle(request);\r\n }\r\n\r\n // Proactively ensure the token is valid (refreshing first if it's\r\n // expired or within the refresh window) before attaching the header.\r\n // This avoids sending a guaranteed-401 request after the app has been\r\n // idle/suspended long enough for the in-memory refresh timer to miss it.\r\n return this.tokenService.ensureValidToken().pipe(\r\n switchMap(valid => {\r\n if (!valid) {\r\n this.redirectToSSO();\r\n return throwError(() => new Error('Authentication required'));\r\n }\r\n\r\n const modifiedRequest = this.addAuthorizationHeader(request);\r\n\r\n return next.handle(modifiedRequest).pipe(\r\n catchError((error: HttpErrorResponse) => {\r\n // Handle 401 Unauthorized errors\r\n if (error.status === 401) {\r\n return this.handle401Error(request, next);\r\n }\r\n\r\n // Handle 403 Forbidden errors\r\n if (error.status === 403) {\r\n console.warn('MedHasso Auth: Access forbidden (403)', error);\r\n }\r\n\r\n return throwError(() => error);\r\n })\r\n );\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * Check if request URL should be excluded from authentication\r\n */\r\n private shouldExcludeRequest(request: HttpRequest<any>): boolean {\r\n const excludeUrls = this.config.interceptorConfig?.excludeUrls || [];\r\n return excludeUrls.some(url => this.matchesUrl(request.url, url));\r\n }\r\n\r\n /**\r\n * Check if request URL should be included for authentication\r\n * If includeUrls is not specified, all requests are included (unless excluded)\r\n */\r\n private shouldIncludeRequest(request: HttpRequest<any>): boolean {\r\n const includeUrls = this.config.interceptorConfig?.includeUrls;\r\n \r\n // If no includeUrls specified, include all requests\r\n if (!includeUrls || includeUrls.length === 0) {\r\n return true;\r\n }\r\n\r\n // If includeUrls specified, only include matching requests\r\n return includeUrls.some(url => this.matchesUrl(request.url, url));\r\n }\r\n\r\n /**\r\n * Check if request URL matches the pattern\r\n */\r\n private matchesUrl(requestUrl: string, pattern: string): boolean {\r\n // Support wildcards and exact matches\r\n if (pattern.includes('*')) {\r\n const regex = new RegExp(pattern.replace(/\\*/g, '.*'));\r\n return regex.test(requestUrl);\r\n }\r\n \r\n return requestUrl.includes(pattern);\r\n }\r\n\r\n /**\r\n * Add authorization header to request\r\n */\r\n private addAuthorizationHeader(request: HttpRequest<any>): HttpRequest<any> {\r\n const authHeader = this.tokenService.getAuthorizationHeader();\r\n \r\n if (!authHeader) {\r\n return request;\r\n }\r\n\r\n return request.clone({\r\n setHeaders: {\r\n Authorization: authHeader\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Handle 401 Unauthorized errors by attempting token refresh\r\n */\r\n private handle401Error(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {\r\n const tokenInfo = this.tokenService.getTokenInfo();\r\n \r\n // If no token or token is expired, redirect to SSO\r\n if (!tokenInfo || tokenInfo.isExpired) {\r\n this.redirectToSSO();\r\n return throwError(() => new Error('Authentication required'));\r\n }\r\n\r\n // If token exists but request failed, try to refresh\r\n return this.tokenService.refreshToken().pipe(\r\n switchMap(() => {\r\n // Retry the original request with new token\r\n const retryRequest = this.addAuthorizationHeader(request);\r\n return next.handle(retryRequest);\r\n }),\r\n catchError((refreshError) => {\r\n // If refresh fails, redirect to SSO\r\n console.error('MedHasso Auth: Token refresh failed', refreshError);\r\n this.redirectToSSO();\r\n return throwError(() => refreshError);\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * Redirect to SSO for authentication\r\n */\r\n private redirectToSSO(): void {\r\n // Add a small delay to prevent multiple redirects\r\n setTimeout(() => {\r\n this.tokenService.redirectToSSO();\r\n }, 100);\r\n }\r\n}","import { Injectable, Inject } from '@angular/core';\r\nimport { HttpClient } from '@angular/common/http';\r\nimport { Observable, of } from 'rxjs';\r\nimport { map, catchError } from 'rxjs/operators';\r\nimport { MedHassoConfig, MedHassoUser } from '../interfaces/medhasso-config.interface';\r\nimport { MEDHASSO_CONFIG } from '../tokens/medhasso-config.token';\r\nimport { TokenManagementService } from './token-management.service';\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class MedHassoAuthService {\r\n\r\n constructor(\r\n @Inject(MEDHASSO_CONFIG) private config: MedHassoConfig,\r\n private tokenService: TokenManagementService,\r\n private http: HttpClient\r\n ) {}\r\n\r\n /**\r\n * Get current authentication status\r\n */\r\n isAuthenticated(): boolean {\r\n return this.tokenService.isAuthenticated();\r\n }\r\n\r\n /**\r\n * Get current user information\r\n */\r\n getCurrentUser(): MedHassoUser | null {\r\n return this.tokenService.getCurrentUser();\r\n }\r\n\r\n /**\r\n * Get current user as observable\r\n */\r\n getCurrentUser$(): Observable<MedHassoUser | null> {\r\n return this.tokenService.user$;\r\n }\r\n\r\n /**\r\n * Check if user has specific role\r\n */\r\n hasRole(role: string): boolean {\r\n return this.tokenService.hasRole(role);\r\n }\r\n\r\n /**\r\n * Check if user has any of the specified roles\r\n */\r\n hasAnyRole(roles: string[]): boolean {\r\n return roles.some(role => this.tokenService.hasRole(role));\r\n }\r\n\r\n /**\r\n * Check if user has all of the specified roles\r\n */\r\n hasAllRoles(roles: string[]): boolean {\r\n return roles.every(role => this.tokenService.hasRole(role));\r\n }\r\n\r\n /**\r\n * Check if user is admin\r\n */\r\n isAdmin(): boolean {\r\n return this.tokenService.isAdmin();\r\n }\r\n\r\n /**\r\n * Login user (redirect to SSO)\r\n */\r\n login(returnUrl?: string): void {\r\n this.tokenService.redirectToSSO(returnUrl);\r\n }\r\n\r\n /**\r\n * Logout user\r\n */\r\n logout(): void {\r\n this.tokenService.logout();\r\n }\r\n\r\n /**\r\n * Refresh authentication token\r\n */\r\n refreshToken(): Observable<any> {\r\n return this.tokenService.refreshToken();\r\n }\r\n\r\n /**\r\n * Check access permissions for a simple key (if applicable)\r\n */\r\n checkAccess(simpleKey: string, backendUrl?: string): Observable<any> {\r\n const url = backendUrl || this.getBackendUrl();\r\n if (!url) {\r\n return of(null);\r\n }\r\n\r\n return this.http.get(`${url}/auth/guard?simple_key=${simpleKey}`)\r\n .pipe(\r\n catchError(error => {\r\n console.error('Access check failed:', error);\r\n return of(null);\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * Check if user is admin via backend API (if applicable)\r\n */\r\n checkIsAdmin(backendUrl?: string): Observable<boolean> {\r\n const user = this.getCurrentUser();\r\n const url = backendUrl || this.getBackendUrl();\r\n \r\n if (!user?.upn || !url) {\r\n return of(false);\r\n }\r\n\r\n return this.http.get(`${url}/isAdmin?email=${user.upn}`)\r\n .pipe(\r\n map((response: any) => !!response),\r\n catchError(() => of(false))\r\n );\r\n }\r\n\r\n /**\r\n * Get backend URL from config (helper method)\r\n */\r\n private getBackendUrl(): string | null {\r\n // This would need to be configured or passed in\r\n // For now, return null as it's application-specific\r\n return null;\r\n }\r\n\r\n /**\r\n * Get token information\r\n */\r\n getTokenInfo() {\r\n return this.tokenService.getTokenInfo();\r\n }\r\n\r\n /**\r\n * Get token as observable\r\n */\r\n getToken$() {\r\n return this.tokenService.token$;\r\n }\r\n\r\n /**\r\n * Handle authentication callback\r\n */\r\n handleAuthCallback(params: { idToken?: string; refreshToken?: string }): boolean {\r\n return this.tokenService.handleAuthCallback(params);\r\n }\r\n\r\n /**\r\n * Get user email\r\n */\r\n getUserEmail(): string | null {\r\n return this.getCurrentUser()?.upn || null;\r\n }\r\n\r\n /**\r\n * Get user display name\r\n */\r\n getUserDisplayName(): string | null {\r\n return this.getCurrentUser()?.name || null;\r\n }\r\n\r\n /**\r\n * Get all user roles\r\n */\r\n getUserRoles(): string[] {\r\n return this.getCurrentUser()?.roles || [];\r\n }\r\n\r\n /**\r\n * Get authorization header value\r\n */\r\n getAuthorizationHeader(): string | null {\r\n return this.tokenService.getAuthorizationHeader();\r\n }\r\n}","import { NgModule, ModuleWithProviders, Provider } from '@angular/core';\r\nimport { HTTP_INTERCEPTORS } from '@angular/common/http';\r\nimport { MedHassoConfig } from './interfaces/medhasso-config.interface';\r\nimport { MEDHASSO_CONFIG } from './tokens/medhasso-config.token';\r\nimport { MedHassoAuthInterceptor } from './interceptors/medhasso-auth.interceptor';\r\nimport { TokenManagementService } from './services/token-management.service';\r\nimport { MedHassoAuthService } from './services/medhasso-auth.service';\r\n\r\n@NgModule({\r\n declarations: [],\r\n imports: [],\r\n exports: []\r\n})\r\nexport class MedHassoAuthModule {\r\n \r\n /**\r\n * Configure the MedHasso authentication module with required settings\r\n */\r\n static forRoot(config: MedHassoConfig): ModuleWithProviders<MedHassoAuthModule> {\r\n return {\r\n ngModule: MedHassoAuthModule,\r\n providers: [\r\n {\r\n provide: MEDHASSO_CONFIG,\r\n useValue: this.mergeWithDefaults(config)\r\n },\r\n TokenManagementService,\r\n MedHassoAuthService,\r\n {\r\n provide: HTTP_INTERCEPTORS,\r\n useClass: MedHassoAuthInterceptor,\r\n multi: true\r\n }\r\n ]\r\n };\r\n }\r\n\r\n /**\r\n * Configure the MedHasso authentication module for feature modules\r\n */\r\n static forChild(): ModuleWithProviders<MedHassoAuthModule> {\r\n return {\r\n ngModule: MedHassoAuthModule,\r\n providers: []\r\n };\r\n }\r\n\r\n /**\r\n * Provide MedHasso authentication with standalone components\r\n */\r\n static forStandalone(config: MedHassoConfig): Provider[] {\r\n return [\r\n {\r\n provide: MEDHASSO_CONFIG,\r\n useValue: this.mergeWithDefaults(config)\r\n },\r\n TokenManagementService,\r\n MedHassoAuthService,\r\n {\r\n provide: HTTP_INTERCEPTORS,\r\n useClass: MedHassoAuthInterceptor,\r\n multi: true\r\n }\r\n ];\r\n }\r\n\r\n /**\r\n * Merge user configuration with sensible defaults\r\n */\r\n private static mergeWithDefaults(config: MedHassoConfig): MedHassoConfig {\r\n const defaultConfig: Partial<MedHassoConfig> = {\r\n tokenConfig: {\r\n storageType: 'localStorage',\r\n idTokenKey: 'idToken',\r\n refreshTokenKey: 'refreshToken',\r\n refreshBeforeExpiryMinutes: 5,\r\n autoRefresh: true,\r\n ...config.tokenConfig\r\n },\r\n interceptorConfig: {\r\n enabled: true,\r\n authHeaderPrefix: 'Bearer',\r\n excludeUrls: [],\r\n includeUrls: [],\r\n ...config.interceptorConfig\r\n },\r\n securityConfig: {\r\n httpsOnly: true,\r\n secureCookies: true,\r\n additionalCspSources: [],\r\n ...config.securityConfig\r\n }\r\n };\r\n\r\n return {\r\n ...config,\r\n ...defaultConfig,\r\n tokenConfig: {\r\n ...defaultConfig.tokenConfig,\r\n ...config.tokenConfig\r\n },\r\n interceptorConfig: {\r\n ...defaultConfig.interceptorConfig,\r\n ...config.interceptorConfig\r\n },\r\n securityConfig: {\r\n ...defaultConfig.securityConfig,\r\n ...config.securityConfig\r\n }\r\n };\r\n }\r\n}","import { Injectable } from '@angular/core';\r\nimport { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, UrlTree } from '@angular/router';\r\nimport { Observable, of } from 'rxjs';\r\nimport { map, catchError } from 'rxjs/operators';\r\nimport { TokenManagementService } from '../services/token-management.service';\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class MedHassoAuthGuard implements CanActivate, CanActivateChild {\r\n\r\n constructor(\r\n private tokenService: TokenManagementService,\r\n private router: Router\r\n ) {}\r\n\r\n canActivate(route: ActivatedRouteSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {\r\n return this.checkAuthentication(route);\r\n }\r\n\r\n canActivateChild(childRoute: ActivatedRouteSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {\r\n return this.checkAuthentication(childRoute);\r\n }\r\n\r\n private checkAuthentication(route: ActivatedRouteSnapshot): Observable<boolean | UrlTree> {\r\n // Check for authentication callback parameters\r\n if (this.handleAuthCallback(route)) {\r\n return of(true);\r\n }\r\n\r\n // Re-syncs from storage and refreshes a stale/expired token before\r\n // deciding, so this guard shares the same de-duplicated refresh and\r\n // storage-drift handling as the HTTP interceptor.\r\n return this.tokenService.ensureValidToken().pipe(\r\n map(valid => {\r\n if (!valid) {\r\n this.tokenService.redirectToSSO();\r\n return false;\r\n }\r\n return true;\r\n }),\r\n catchError(() => {\r\n this.tokenService.redirectToSSO();\r\n return of(false);\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * Handle authentication callback from MedHasso SSO\r\n */\r\n private handleAuthCallback(route: ActivatedRouteSnapshot): boolean {\r\n const queryParams = route.queryParams;\r\n \r\n // Check for AIRO redirect flow\r\n if (queryParams['redirectapp'] === 'airo' && queryParams['idToken']) {\r\n return this.tokenService.handleAuthCallback({\r\n idToken: queryParams['idToken'],\r\n refreshToken: queryParams['refreshToken']\r\n });\r\n }\r\n\r\n // Check for standard authentication callback\r\n if (queryParams['idToken'] || queryParams['token']) {\r\n return this.tokenService.handleAuthCallback({\r\n idToken: queryParams['idToken'] || queryParams['token'],\r\n refreshToken: queryParams['refreshToken']\r\n });\r\n }\r\n\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Role-based authentication guard\r\n */\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class MedHassoRoleGuard implements CanActivate, CanActivateChild {\r\n\r\n constructor(\r\n private tokenService: TokenManagementService,\r\n private router: Router\r\n ) {}\r\n\r\n canActivate(route: ActivatedRouteSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {\r\n return this.checkRole(route);\r\n }\r\n\r\n canActivateChild(childRoute: ActivatedRouteSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {\r\n return this.checkRole(childRoute);\r\n }\r\n\r\n private checkRole(route: ActivatedRouteSnapshot): boolean | UrlTree {\r\n const requiredRoles = route.data['roles'] as string[];\r\n const requiredRole = route.data['role'] as string;\r\n\r\n if (!this.tokenService.isAuthenticated()) {\r\n this.tokenService.redirectToSSO();\r\n return false;\r\n }\r\n\r\n // Check multiple roles (OR condition)\r\n if (requiredRoles && requiredRoles.length > 0) {\r\n const hasAnyRole = requiredRoles.some(role => this.tokenService.hasRole(role));\r\n if (!hasAnyRole) {\r\n return this.router.createUrlTree(['/unauthorized']);\r\n }\r\n }\r\n\r\n // Check single role\r\n if (requiredRole && !this.tokenService.hasRole(requiredRole)) {\r\n return this.router.createUrlTree(['/unauthorized']);\r\n }\r\n\r\n return true;\r\n }\r\n}\r\n\r\n/**\r\n * Admin-only authentication guard\r\n */\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class MedHassoAdminGuard implements CanActivate, CanActivateChild {\r\n\r\n constructor(\r\n private tokenService: TokenManagementService,\r\n private router: Router\r\n ) {}\r\n\r\n canActivate(): boolean | UrlTree {\r\n return this.checkAdmin();\r\n }\r\n\r\n canActivateChild(): boolean | UrlTree {\r\n return this.checkAdmin();\r\n }\r\n\r\n private checkAdmin(): boolean | UrlTree {\r\n if (!this.tokenService.isAuthenticated()) {\r\n this.tokenService.redirectToSSO();\r\n return false;\r\n }\r\n\r\n if (!this.tokenService.isAdmin()) {\r\n return this.router.createUrlTree(['/unauthorized']);\r\n }\r\n\r\n return true;\r\n }\r\n}","/*\r\n * Public API Surface of medhasso-auth\r\n */\r\n\r\n// Module\r\nexport * from './lib/medhasso-auth.module';\r\n\r\n// Services\r\nexport * from './lib/services/token-management.service';\r\nexport * from './lib/services/medhasso-auth.service';\r\n\r\n// Guards\r\nexport * from './lib/guards/medhasso-auth.guard';\r\n\r\n// Interceptors\r\nexport * from './lib/interceptors/medhasso-auth.interceptor';\r\n\r\n// Interfaces\r\nexport * from './lib/interfaces/medhasso-config.interface';\r\n\r\n// Tokens\r\nexport * from './lib/tokens/medhasso-config.token';","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.TokenManagementService","i2"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"medha-analytics-medhasso-auth.mjs","sources":["../../../projects/medhasso-auth/src/lib/tokens/medhasso-config.token.ts","../../../projects/medhasso-auth/src/lib/services/token-management.service.ts","../../../projects/medhasso-auth/src/lib/interceptors/medhasso-auth.interceptor.ts","../../../projects/medhasso-auth/src/lib/services/medhasso-auth.service.ts","../../../projects/medhasso-auth/src/lib/medhasso-auth.module.ts","../../../projects/medhasso-auth/src/lib/guards/medhasso-auth.guard.ts","../../../projects/medhasso-auth/src/public-api.ts","../../../projects/medhasso-auth/src/medha-analytics-medhasso-auth.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport { MedHassoConfig } from '../interfaces/medhasso-config.interface';\n\nexport const MEDHASSO_CONFIG = new InjectionToken<MedHassoConfig>('MEDHASSO_CONFIG');","import { Injectable, Inject } from '@angular/core';\nimport { HttpBackend, HttpClient } from '@angular/common/http';\nimport { BehaviorSubject, Observable, of, throwError, timer } from 'rxjs';\nimport { catchError, finalize, map, shareReplay, tap } from 'rxjs/operators';\nimport { MedHassoConfig, TokenConfig, TokenInfo, MedHassoUser } from '../interfaces/medhasso-config.interface';\nimport { MEDHASSO_CONFIG } from '../tokens/medhasso-config.token';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class TokenManagementService {\n private refreshTokenInterval: any;\n private refreshInProgress$: Observable<any> | null = null;\n private tokenSubject = new BehaviorSubject<TokenInfo | null>(null);\n private userSubject = new BehaviorSubject<MedHassoUser | null>(null);\n /**\n * Dedicated HttpClient wired directly to HttpBackend so the refresh POST\n * bypasses the HTTP_INTERCEPTORS chain. Routing refresh through the normal\n * http (which runs MedHassoAuthInterceptor) causes self-interception:\n * interceptor → ensureValidToken → refreshToken → returns the in-flight\n * refreshInProgress$ which is itself → deadlock, with finalize never firing\n * so refreshInProgress$ stays pinned and every subsequent request hangs too.\n */\n private readonly refreshHttp: HttpClient;\n private readonly onVisibilityChange = () => {\n if (document.visibilityState === 'visible') {\n this.ensureValidToken().subscribe();\n }\n };\n private readonly onWindowFocus = () => {\n this.ensureValidToken().subscribe();\n };\n\n public token$ = this.tokenSubject.asObservable();\n public user$ = this.userSubject.asObservable();\n\n private get tokenConfig(): TokenConfig {\n return this.config.tokenConfig || {};\n }\n\n private get storageType(): 'localStorage' | 'sessionStorage' {\n return this.tokenConfig.storageType || 'localStorage';\n }\n\n private get idTokenKey(): string {\n return this.tokenConfig.idTokenKey || 'idToken';\n }\n\n private get refreshTokenKey(): string {\n return this.tokenConfig.refreshTokenKey || 'refreshToken';\n }\n\n private get refreshBeforeExpiryMinutes(): number {\n return this.tokenConfig.refreshBeforeExpiryMinutes || 5;\n }\n\n private get autoRefresh(): boolean {\n return this.tokenConfig.autoRefresh !== false;\n }\n\n constructor(\n @Inject(MEDHASSO_CONFIG) private config: MedHassoConfig,\n private http: HttpClient,\n httpBackend: HttpBackend\n ) {\n this.refreshHttp = new HttpClient(httpBackend);\n this.initializeFromStorage();\n this.setupWakeDetection();\n }\n\n /**\n * Re-validate the token whenever the tab regains focus/visibility.\n * The refresh timer is a JS timer and does not fire while the OS/browser\n * was suspended (e.g. laptop sleep overnight), so it can't be relied on\n * alone to catch a token that went stale while the app was inactive.\n */\n private setupWakeDetection(): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') {\n return;\n }\n\n document.addEventListener('visibilitychange', this.onVisibilityChange);\n window.addEventListener('focus', this.onWindowFocus);\n }\n\n /**\n * Remove wake-detection listeners registered in setupWakeDetection().\n */\n private teardownWakeDetection(): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') {\n return;\n }\n\n document.removeEventListener('visibilitychange', this.onVisibilityChange);\n window.removeEventListener('focus', this.onWindowFocus);\n }\n\n /**\n * Initialize token information from storage\n */\n private initializeFromStorage(): void {\n const token = this.getStoredToken();\n if (token) {\n const tokenInfo = this.createTokenInfo(token);\n this.tokenSubject.next(tokenInfo);\n this.userSubject.next(this.extractUserFromToken(token));\n \n if (this.autoRefresh && !tokenInfo.isExpired) {\n this.setTokenRefreshInterval(tokenInfo);\n }\n }\n }\n\n /**\n * Re-sync in-memory token state from storage if it has drifted (e.g.\n * something wrote tokens directly to storage without going through\n * setTokens()). No-op when storage already matches memory.\n */\n private syncFromStorage(): void {\n const storedToken = this.getStoredToken();\n const currentToken = this.getTokenInfo()?.token ?? null;\n\n if (storedToken === currentToken) {\n return;\n }\n\n if (!storedToken) {\n this.tokenSubject.next(null);\n this.userSubject.next(null);\n this.clearTokenRefreshInterval();\n return;\n }\n\n const tokenInfo = this.createTokenInfo(storedToken);\n this.tokenSubject.next(tokenInfo);\n this.userSubject.next(this.extractUserFromToken(storedToken));\n\n if (this.autoRefresh && !tokenInfo.isExpired) {\n this.setTokenRefreshInterval(tokenInfo);\n }\n }\n\n /**\n * Get stored token from configured storage\n */\n private getStoredToken(): string | null {\n const storage = this.storageType === 'localStorage' ? localStorage : sessionStorage;\n return storage.getItem(this.idTokenKey);\n }\n\n /**\n * Get stored refresh token from configured storage\n */\n private getStoredRefreshToken(): string | null {\n const storage = this.storageType === 'localStorage' ? localStorage : sessionStorage;\n return storage.getItem(this.refreshTokenKey);\n }\n\n /**\n * Store tokens in configured storage\n */\n private storeTokens(idToken: string, refreshToken?: string): void {\n const storage = this.storageType === 'localStorage' ? localStorage : sessionStorage;\n storage.setItem(this.idTokenKey, idToken);\n if (refreshToken) {\n storage.setItem(this.refreshTokenKey, refreshToken);\n }\n }\n\n /**\n * Clear stored tokens\n */\n private clearStoredTokens(): void {\n const storage = this.storageType === 'localStorage' ? localStorage : sessionStorage;\n storage.removeItem(this.idTokenKey);\n storage.removeItem(this.refreshTokenKey);\n }\n\n /**\n * Create token info object from JWT token.\n *\n * `isExpired` and `needsRefresh` are getters, not snapshotted booleans:\n * the TokenInfo object may outlive wall-clock events that change its\n * expiry status (e.g. OS/browser suspended overnight, laptop sleep).\n * A snapshotted boolean captured at creation time would then lie — the\n * wake handler would call ensureValidToken(), see `isExpired: false`\n * from the pre-sleep snapshot, and skip the refresh. Reading these as\n * getters means every access reflects the current clock.\n */\n private createTokenInfo(token: string): TokenInfo {\n const payload = this.decodeJwt(token);\n const expiresAt = payload.exp * 1000; // Convert to milliseconds\n const refreshWindow = this.refreshBeforeExpiryMinutes * 60 * 1000;\n\n return {\n token,\n payload,\n expiresAt,\n get isExpired() { return Date.now() >= expiresAt; },\n get needsRefresh() { return Date.now() >= (expiresAt - refreshWindow); }\n };\n }\n\n /**\n * Decode JWT token payload\n */\n private decodeJwt(token: string): any {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new Error('Invalid JWT token format');\n }\n const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));\n return payload;\n } catch (error) {\n console.error('Error decoding JWT token:', error);\n throw new Error('Invalid JWT token');\n }\n }\n\n /**\n * Extract user information from JWT token\n */\n private extractUserFromToken(token: string): MedHassoUser {\n const payload = this.decodeJwt(token);\n return {\n upn: payload.upn || payload.email,\n name: payload.name,\n roles: payload.roles || [],\n ...payload\n };\n }\n\n /**\n * Set up automatic token refresh interval\n */\n private setTokenRefreshInterval(tokenInfo: TokenInfo): void {\n this.clearTokenRefreshInterval();\n\n if (!this.autoRefresh || tokenInfo.isExpired) {\n return;\n }\n\n const remainingTime = tokenInfo.expiresAt - Date.now();\n const refreshTime = remainingTime - (this.refreshBeforeExpiryMinutes * 60 * 1000);\n\n if (refreshTime > 0) {\n this.refreshTokenInterval = timer(refreshTime).subscribe(() => {\n this.refreshToken().subscribe();\n });\n } else {\n // Token needs immediate refresh\n this.refreshToken().subscribe();\n }\n }\n\n /**\n * Clear token refresh interval\n */\n private clearTokenRefreshInterval(): void {\n if (this.refreshTokenInterval) {\n this.refreshTokenInterval.unsubscribe();\n this.refreshTokenInterval = null;\n }\n }\n\n /**\n * Get current token information\n */\n getTokenInfo(): TokenInfo | null {\n return this.tokenSubject.value;\n }\n\n /**\n * Get current user information\n */\n getCurrentUser(): MedHassoUser | null {\n return this.userSubject.value;\n }\n\n /**\n * Check if user is authenticated\n */\n isAuthenticated(): boolean {\n const tokenInfo = this.getTokenInfo();\n return tokenInfo !== null && !tokenInfo.isExpired;\n }\n\n /**\n * Check if user has specific role\n */\n hasRole(role: string): boolean {\n const user = this.getCurrentUser();\n return user?.roles?.includes(role) || false;\n }\n\n /**\n * Check if user is admin\n */\n isAdmin(): boolean {\n return this.hasRole('admin') || this.hasRole('administrator');\n }\n\n /**\n * Set tokens (typically called after successful authentication)\n */\n setTokens(idToken: string, refreshToken?: string): void {\n this.storeTokens(idToken, refreshToken);\n const tokenInfo = this.createTokenInfo(idToken);\n const user = this.extractUserFromToken(idToken);\n\n this.tokenSubject.next(tokenInfo);\n this.userSubject.next(user);\n\n if (this.autoRefresh) {\n this.setTokenRefreshInterval(tokenInfo);\n }\n }\n\n /**\n * Refresh the current token.\n * Concurrent callers (auto-refresh timer, wake detection, interceptor,\n * route guard) share the same in-flight request instead of each firing\n * their own POST to the refresh endpoint.\n */\n refreshToken(): Observable<any> {\n if (this.refreshInProgress$) {\n return this.refreshInProgress$;\n }\n\n const refreshToken = this.getStoredRefreshToken();\n\n if (!refreshToken) {\n return throwError(() => new Error('No refresh token available'));\n }\n\n const request$ = this.refreshHttp.post(`${this.config.refreshUrl}/api/refresh-token`, { refreshToken })\n .pipe(\n tap((response: any) => {\n if (response.id_token) {\n this.setTokens(response.id_token, response.refresh_token);\n }\n }),\n catchError(error => {\n console.error('Token refresh failed:', error);\n // Only clear the session on a definitive auth failure from the refresh\n // endpoint (dead/revoked/invalid refresh token). Transient failures —\n // network blips, 5xx, timeouts — must NOT force a logout: the current\n // id_token may still be valid for the rest of its lifetime, and the\n // background-refresh path (ensureValidToken → needsRefresh) runs on\n // every request within the 5-min window, so a single network hiccup\n // would otherwise log the user out mid-session.\n const status = error?.status;\n if (status === 400 || status === 401) {\n this.logout();\n }\n return throwError(() => error);\n }),\n finalize(() => {\n this.refreshInProgress$ = null;\n }),\n shareReplay({ bufferSize: 1, refCount: false })\n );\n\n this.refreshInProgress$ = request$;\n return request$;\n }\n\n /**\n * Ensure the current token is valid, refreshing it first if it is\n * expired. Resolves `false` if there is no token at all, or the token\n * is expired and refreshing it failed (in both cases the caller should\n * redirect to SSO).\n *\n * A token that merely `needsRefresh` (inside the refresh window, but\n * not yet expired) is still good enough to use: the refresh for it is\n * kicked off in the background so the *current* request/route isn't\n * blocked or logged out by a transient refresh failure (network blip,\n * refresh endpoint hiccup) while the existing token would have worked\n * fine. Only an actually-expired token blocks the caller.\n *\n * Re-syncs from storage first: the in-memory token snapshot is only\n * updated via setTokens()/logout(), so if something wrote directly to\n * storage (e.g. the SSO callback flow) without going through this\n * service, the in-memory copy would otherwise be stale until the next\n * full page load re-runs initializeFromStorage().\n *\n * Anonymous/bootstrap endpoints that must be reachable before any token\n * exists (e.g. the SSO callback's own token-exchange request) should be\n * added to `interceptorConfig.excludeUrls` so they bypass this check\n * entirely, rather than relying on this method to let them through.\n */\n ensureValidToken(): Observable<boolean> {\n this.syncFromStorage();\n const tokenInfo = this.getTokenInfo();\n\n if (!tokenInfo) {\n return of(false);\n }\n\n if (tokenInfo.isExpired) {\n return this.refreshToken().pipe(\n map(() => true),\n catchError(() => of(false))\n );\n }\n\n if (tokenInfo.needsRefresh) {\n this.refreshToken().subscribe({ error: () => {} });\n }\n\n return of(true);\n }\n\n /**\n * Logout user and clear all tokens\n */\n logout(): void {\n this.clearTokenRefreshInterval();\n this.clearStoredTokens();\n this.tokenSubject.next(null);\n this.userSubject.next(null);\n }\n\n /**\n * Redirect to MedHasso SSO for authentication\n */\n redirectToSSO(returnUrl?: string): void {\n const currentUrl = returnUrl || window.location.href;\n const ssoUrl = `${this.config.ssoUrl}?url=${encodeURIComponent(currentUrl)}&applicationName=${encodeURIComponent(this.config.applicationName)}`;\n window.location.href = ssoUrl;\n }\n\n /**\n * Handle authentication callback (typically from SSO redirect)\n */\n handleAuthCallback(params: { idToken?: string; refreshToken?: string }): boolean {\n if (params.idToken) {\n this.setTokens(params.idToken, params.refreshToken);\n return true;\n }\n return false;\n }\n\n /**\n * Get authorization header value\n */\n getAuthorizationHeader(): string | null {\n const tokenInfo = this.getTokenInfo();\n if (tokenInfo && !tokenInfo.isExpired) {\n const prefix = this.config.interceptorConfig?.authHeaderPrefix || 'Bearer';\n return `${prefix} ${tokenInfo.token}`;\n }\n return null;\n }\n\n /**\n * Cleanup on service destruction\n */\n ngOnDestroy(): void {\n this.clearTokenRefreshInterval();\n this.teardownWakeDetection();\n }\n}\n","import { Injectable, Inject } from '@angular/core';\nimport { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';\nimport { Observable, throwError } from 'rxjs';\nimport { catchError, switchMap } from 'rxjs/operators';\nimport { MedHassoConfig } from '../interfaces/medhasso-config.interface';\nimport { MEDHASSO_CONFIG } from '../tokens/medhasso-config.token';\nimport { TokenManagementService } from '../services/token-management.service';\n\n@Injectable()\nexport class MedHassoAuthInterceptor implements HttpInterceptor {\n\n constructor(\n @Inject(MEDHASSO_CONFIG) private config: MedHassoConfig,\n private tokenService: TokenManagementService\n ) {}\n\n intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {\n // Check if interceptor is enabled\n if (this.config.interceptorConfig?.enabled === false) {\n return next.handle(request);\n }\n\n // Check if request should be excluded\n if (this.shouldExcludeRequest(request)) {\n return next.handle(request);\n }\n\n // Check if request should be included (if includeUrls is specified)\n if (!this.shouldIncludeRequest(request)) {\n return next.handle(request);\n }\n\n // Proactively ensure the token is valid (refreshing first if it's\n // expired or within the refresh window) before attaching the header.\n // This avoids sending a guaranteed-401 request after the app has been\n // idle/suspended long enough for the in-memory refresh timer to miss it.\n return this.tokenService.ensureValidToken().pipe(\n switchMap(valid => {\n if (!valid) {\n this.redirectToSSO();\n return throwError(() => new Error('Authentication required'));\n }\n\n const modifiedRequest = this.addAuthorizationHeader(request);\n\n return next.handle(modifiedRequest).pipe(\n catchError((error: HttpErrorResponse) => {\n // Handle 401 Unauthorized errors\n if (error.status === 401) {\n return this.handle401Error(request, next);\n }\n\n // Handle 403 Forbidden errors\n if (error.status === 403) {\n console.warn('MedHasso Auth: Access forbidden (403)', error);\n }\n\n return throwError(() => error);\n })\n );\n })\n );\n }\n\n /**\n * Check if request URL should be excluded from authentication\n */\n private shouldExcludeRequest(request: HttpRequest<any>): boolean {\n const excludeUrls = this.config.interceptorConfig?.excludeUrls || [];\n return excludeUrls.some(url => this.matchesUrl(request.url, url));\n }\n\n /**\n * Check if request URL should be included for authentication\n * If includeUrls is not specified, all requests are included (unless excluded)\n */\n private shouldIncludeRequest(request: HttpRequest<any>): boolean {\n const includeUrls = this.config.interceptorConfig?.includeUrls;\n \n // If no includeUrls specified, include all requests\n if (!includeUrls || includeUrls.length === 0) {\n return true;\n }\n\n // If includeUrls specified, only include matching requests\n return includeUrls.some(url => this.matchesUrl(request.url, url));\n }\n\n /**\n * Check if request URL matches the pattern\n */\n private matchesUrl(requestUrl: string, pattern: string): boolean {\n // Support wildcards and exact matches\n if (pattern.includes('*')) {\n const regex = new RegExp(pattern.replace(/\\*/g, '.*'));\n return regex.test(requestUrl);\n }\n \n return requestUrl.includes(pattern);\n }\n\n /**\n * Add authorization header to request\n */\n private addAuthorizationHeader(request: HttpRequest<any>): HttpRequest<any> {\n const authHeader = this.tokenService.getAuthorizationHeader();\n \n if (!authHeader) {\n return request;\n }\n\n return request.clone({\n setHeaders: {\n Authorization: authHeader\n }\n });\n }\n\n /**\n * Handle 401 Unauthorized errors by attempting token refresh.\n *\n * Always tries the refresh path first, even when the in-memory token looks\n * expired to our clock. The server already said \"unauthorized\" — whether our\n * local isExpired check agrees doesn't change what we need to do next.\n * Short-circuiting to SSO on `tokenInfo.isExpired` would bounce the user on\n * mid-flight expirations and small client/server clock skews, cases where the\n * refresh token is still perfectly valid and would have recovered. If the\n * refresh really can't succeed (no refresh token in storage, dead refresh\n * token → 400/401), refreshToken() throws and the catchError below redirects.\n */\n private handle401Error(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {\n // catchError sits BEFORE switchMap on purpose: it must only catch failures\n // from refreshToken(), not from the retried next.handle(retryRequest). If\n // the refresh succeeds but the retry fails (403, 500, etc.), that error\n // has nothing to do with authentication — redirecting to SSO would be\n // wrong. Keep the SSO redirect scoped to the \"refresh really can't help\"\n // case; let any retry error propagate normally to the component.\n return this.tokenService.refreshToken().pipe(\n catchError((refreshError) => {\n console.error('MedHasso Auth: Token refresh failed', refreshError);\n this.redirectToSSO();\n return throwError(() => refreshError);\n }),\n switchMap(() => {\n // Retry the original request with new token\n const retryRequest = this.addAuthorizationHeader(request);\n return next.handle(retryRequest);\n })\n );\n }\n\n /**\n * Redirect to SSO for authentication\n */\n private redirectToSSO(): void {\n // Add a small delay to prevent multiple redirects\n setTimeout(() => {\n this.tokenService.redirectToSSO();\n }, 100);\n }\n}","import { Injectable, Inject } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { Observable, of } from 'rxjs';\nimport { map, catchError } from 'rxjs/operators';\nimport { MedHassoConfig, MedHassoUser } from '../interfaces/medhasso-config.interface';\nimport { MEDHASSO_CONFIG } from '../tokens/medhasso-config.token';\nimport { TokenManagementService } from './token-management.service';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class MedHassoAuthService {\n\n constructor(\n @Inject(MEDHASSO_CONFIG) private config: MedHassoConfig,\n private tokenService: TokenManagementService,\n private http: HttpClient\n ) {}\n\n /**\n * Get current authentication status\n */\n isAuthenticated(): boolean {\n return this.tokenService.isAuthenticated();\n }\n\n /**\n * Get current user information\n */\n getCurrentUser(): MedHassoUser | null {\n return this.tokenService.getCurrentUser();\n }\n\n /**\n * Get current user as observable\n */\n getCurrentUser$(): Observable<MedHassoUser | null> {\n return this.tokenService.user$;\n }\n\n /**\n * Check if user has specific role\n */\n hasRole(role: string): boolean {\n return this.tokenService.hasRole(role);\n }\n\n /**\n * Check if user has any of the specified roles\n */\n hasAnyRole(roles: string[]): boolean {\n return roles.some(role => this.tokenService.hasRole(role));\n }\n\n /**\n * Check if user has all of the specified roles\n */\n hasAllRoles(roles: string[]): boolean {\n return roles.every(role => this.tokenService.hasRole(role));\n }\n\n /**\n * Check if user is admin\n */\n isAdmin(): boolean {\n return this.tokenService.isAdmin();\n }\n\n /**\n * Login user (redirect to SSO)\n */\n login(returnUrl?: string): void {\n this.tokenService.redirectToSSO(returnUrl);\n }\n\n /**\n * Logout user\n */\n logout(): void {\n this.tokenService.logout();\n }\n\n /**\n * Refresh authentication token\n */\n refreshToken(): Observable<any> {\n return this.tokenService.refreshToken();\n }\n\n /**\n * Check access permissions for a simple key (if applicable)\n */\n checkAccess(simpleKey: string, backendUrl?: string): Observable<any> {\n const url = backendUrl || this.getBackendUrl();\n if (!url) {\n return of(null);\n }\n\n return this.http.get(`${url}/auth/guard?simple_key=${simpleKey}`)\n .pipe(\n catchError(error => {\n console.error('Access check failed:', error);\n return of(null);\n })\n );\n }\n\n /**\n * Check if user is admin via backend API (if applicable)\n */\n checkIsAdmin(backendUrl?: string): Observable<boolean> {\n const user = this.getCurrentUser();\n const url = backendUrl || this.getBackendUrl();\n \n if (!user?.upn || !url) {\n return of(false);\n }\n\n return this.http.get(`${url}/isAdmin?email=${user.upn}`)\n .pipe(\n map((response: any) => !!response),\n catchError(() => of(false))\n );\n }\n\n /**\n * Get backend URL from config (helper method)\n */\n private getBackendUrl(): string | null {\n // This would need to be configured or passed in\n // For now, return null as it's application-specific\n return null;\n }\n\n /**\n * Get token information\n */\n getTokenInfo() {\n return this.tokenService.getTokenInfo();\n }\n\n /**\n * Get token as observable\n */\n getToken$() {\n return this.tokenService.token$;\n }\n\n /**\n * Handle authentication callback\n */\n handleAuthCallback(params: { idToken?: string; refreshToken?: string }): boolean {\n return this.tokenService.handleAuthCallback(params);\n }\n\n /**\n * Get user email\n */\n getUserEmail(): string | null {\n return this.getCurrentUser()?.upn || null;\n }\n\n /**\n * Get user display name\n */\n getUserDisplayName(): string | null {\n return this.getCurrentUser()?.name || null;\n }\n\n /**\n * Get all user roles\n */\n getUserRoles(): string[] {\n return this.getCurrentUser()?.roles || [];\n }\n\n /**\n * Get authorization header value\n */\n getAuthorizationHeader(): string | null {\n return this.tokenService.getAuthorizationHeader();\n }\n}","import { NgModule, ModuleWithProviders, Provider } from '@angular/core';\nimport { HTTP_INTERCEPTORS } from '@angular/common/http';\nimport { MedHassoConfig } from './interfaces/medhasso-config.interface';\nimport { MEDHASSO_CONFIG } from './tokens/medhasso-config.token';\nimport { MedHassoAuthInterceptor } from './interceptors/medhasso-auth.interceptor';\nimport { TokenManagementService } from './services/token-management.service';\nimport { MedHassoAuthService } from './services/medhasso-auth.service';\n\n@NgModule({\n declarations: [],\n imports: [],\n exports: []\n})\nexport class MedHassoAuthModule {\n \n /**\n * Configure the MedHasso authentication module with required settings\n */\n static forRoot(config: MedHassoConfig): ModuleWithProviders<MedHassoAuthModule> {\n return {\n ngModule: MedHassoAuthModule,\n providers: [\n {\n provide: MEDHASSO_CONFIG,\n useValue: this.mergeWithDefaults(config)\n },\n TokenManagementService,\n MedHassoAuthService,\n {\n provide: HTTP_INTERCEPTORS,\n useClass: MedHassoAuthInterceptor,\n multi: true\n }\n ]\n };\n }\n\n /**\n * Configure the MedHasso authentication module for feature modules\n */\n static forChild(): ModuleWithProviders<MedHassoAuthModule> {\n return {\n ngModule: MedHassoAuthModule,\n providers: []\n };\n }\n\n /**\n * Provide MedHasso authentication with standalone components\n */\n static forStandalone(config: MedHassoConfig): Provider[] {\n return [\n {\n provide: MEDHASSO_CONFIG,\n useValue: this.mergeWithDefaults(config)\n },\n TokenManagementService,\n MedHassoAuthService,\n {\n provide: HTTP_INTERCEPTORS,\n useClass: MedHassoAuthInterceptor,\n multi: true\n }\n ];\n }\n\n /**\n * Merge user configuration with sensible defaults\n */\n private static mergeWithDefaults(config: MedHassoConfig): MedHassoConfig {\n const defaultConfig: Partial<MedHassoConfig> = {\n tokenConfig: {\n storageType: 'localStorage',\n idTokenKey: 'idToken',\n refreshTokenKey: 'refreshToken',\n refreshBeforeExpiryMinutes: 5,\n autoRefresh: true,\n ...config.tokenConfig\n },\n interceptorConfig: {\n enabled: true,\n authHeaderPrefix: 'Bearer',\n excludeUrls: [],\n includeUrls: [],\n ...config.interceptorConfig\n },\n securityConfig: {\n httpsOnly: true,\n secureCookies: true,\n additionalCspSources: [],\n ...config.securityConfig\n }\n };\n\n return {\n ...config,\n ...defaultConfig,\n tokenConfig: {\n ...defaultConfig.tokenConfig,\n ...config.tokenConfig\n },\n interceptorConfig: {\n ...defaultConfig.interceptorConfig,\n ...config.interceptorConfig\n },\n securityConfig: {\n ...defaultConfig.securityConfig,\n ...config.securityConfig\n }\n };\n }\n}","import { Injectable } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, UrlTree } from '@angular/router';\nimport { Observable, of } from 'rxjs';\nimport { map, catchError } from 'rxjs/operators';\nimport { TokenManagementService } from '../services/token-management.service';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class MedHassoAuthGuard implements CanActivate, CanActivateChild {\n\n constructor(\n private tokenService: TokenManagementService,\n private router: Router\n ) {}\n\n canActivate(route: ActivatedRouteSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {\n return this.checkAuthentication(route);\n }\n\n canActivateChild(childRoute: ActivatedRouteSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {\n return this.checkAuthentication(childRoute);\n }\n\n private checkAuthentication(route: ActivatedRouteSnapshot): Observable<boolean | UrlTree> {\n // Check for authentication callback parameters\n if (this.handleAuthCallback(route)) {\n return of(true);\n }\n\n // Re-syncs from storage and refreshes a stale/expired token before\n // deciding, so this guard shares the same de-duplicated refresh and\n // storage-drift handling as the HTTP interceptor.\n return this.tokenService.ensureValidToken().pipe(\n map(valid => {\n if (!valid) {\n this.tokenService.redirectToSSO();\n return false;\n }\n return true;\n }),\n catchError(() => {\n this.tokenService.redirectToSSO();\n return of(false);\n })\n );\n }\n\n /**\n * Handle authentication callback from MedHasso SSO\n */\n private handleAuthCallback(route: ActivatedRouteSnapshot): boolean {\n const queryParams = route.queryParams;\n \n // Check for AIRO redirect flow\n if (queryParams['redirectapp'] === 'airo' && queryParams['idToken']) {\n return this.tokenService.handleAuthCallback({\n idToken: queryParams['idToken'],\n refreshToken: queryParams['refreshToken']\n });\n }\n\n // Check for standard authentication callback\n if (queryParams['idToken'] || queryParams['token']) {\n return this.tokenService.handleAuthCallback({\n idToken: queryParams['idToken'] || queryParams['token'],\n refreshToken: queryParams['refreshToken']\n });\n }\n\n return false;\n }\n}\n\n/**\n * Role-based authentication guard\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class MedHassoRoleGuard implements CanActivate, CanActivateChild {\n\n constructor(\n private tokenService: TokenManagementService,\n private router: Router\n ) {}\n\n canActivate(route: ActivatedRouteSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {\n return this.checkRole(route);\n }\n\n canActivateChild(childRoute: ActivatedRouteSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {\n return this.checkRole(childRoute);\n }\n\n private checkRole(route: ActivatedRouteSnapshot): boolean | UrlTree {\n const requiredRoles = route.data['roles'] as string[];\n const requiredRole = route.data['role'] as string;\n\n if (!this.tokenService.isAuthenticated()) {\n this.tokenService.redirectToSSO();\n return false;\n }\n\n // Check multiple roles (OR condition)\n if (requiredRoles && requiredRoles.length > 0) {\n const hasAnyRole = requiredRoles.some(role => this.tokenService.hasRole(role));\n if (!hasAnyRole) {\n return this.router.createUrlTree(['/unauthorized']);\n }\n }\n\n // Check single role\n if (requiredRole && !this.tokenService.hasRole(requiredRole)) {\n return this.router.createUrlTree(['/unauthorized']);\n }\n\n return true;\n }\n}\n\n/**\n * Admin-only authentication guard\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class MedHassoAdminGuard implements CanActivate, CanActivateChild {\n\n constructor(\n private tokenService: TokenManagementService,\n private router: Router\n ) {}\n\n canActivate(): boolean | UrlTree {\n return this.checkAdmin();\n }\n\n canActivateChild(): boolean | UrlTree {\n return this.checkAdmin();\n }\n\n private checkAdmin(): boolean | UrlTree {\n if (!this.tokenService.isAuthenticated()) {\n this.tokenService.redirectToSSO();\n return false;\n }\n\n if (!this.tokenService.isAdmin()) {\n return this.router.createUrlTree(['/unauthorized']);\n }\n\n return true;\n }\n}","/*\n * Public API Surface of medhasso-auth\n */\n\n// Module\nexport * from './lib/medhasso-auth.module';\n\n// Services\nexport * from './lib/services/token-management.service';\nexport * from './lib/services/medhasso-auth.service';\n\n// Guards\nexport * from './lib/guards/medhasso-auth.guard';\n\n// Interceptors\nexport * from './lib/interceptors/medhasso-auth.interceptor';\n\n// Interfaces\nexport * from './lib/interfaces/medhasso-config.interface';\n\n// Tokens\nexport * from './lib/tokens/medhasso-config.token';","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.TokenManagementService","i2"],"mappings":"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"}
|
|
@@ -26,7 +26,16 @@ export declare class MedHassoAuthInterceptor implements HttpInterceptor {
|
|
|
26
26
|
*/
|
|
27
27
|
private addAuthorizationHeader;
|
|
28
28
|
/**
|
|
29
|
-
* Handle 401 Unauthorized errors by attempting token refresh
|
|
29
|
+
* Handle 401 Unauthorized errors by attempting token refresh.
|
|
30
|
+
*
|
|
31
|
+
* Always tries the refresh path first, even when the in-memory token looks
|
|
32
|
+
* expired to our clock. The server already said "unauthorized" — whether our
|
|
33
|
+
* local isExpired check agrees doesn't change what we need to do next.
|
|
34
|
+
* Short-circuiting to SSO on `tokenInfo.isExpired` would bounce the user on
|
|
35
|
+
* mid-flight expirations and small client/server clock skews, cases where the
|
|
36
|
+
* refresh token is still perfectly valid and would have recovered. If the
|
|
37
|
+
* refresh really can't succeed (no refresh token in storage, dead refresh
|
|
38
|
+
* token → 400/401), refreshToken() throws and the catchError below redirects.
|
|
30
39
|
*/
|
|
31
40
|
private handle401Error;
|
|
32
41
|
/**
|
|
@@ -110,11 +110,17 @@ export interface TokenInfo {
|
|
|
110
110
|
*/
|
|
111
111
|
expiresAt: number;
|
|
112
112
|
/**
|
|
113
|
-
* Whether token is expired
|
|
113
|
+
* Whether token is expired. Backed by a dynamic getter: every read
|
|
114
|
+
* re-evaluates against the current clock, so the value stays accurate even
|
|
115
|
+
* after a long idle/suspend when nothing has written to storage. Spreading
|
|
116
|
+
* (`{...tokenInfo}`) or `JSON.stringify`-ing a TokenInfo captures a boolean
|
|
117
|
+
* snapshot and loses this behavior — pass the object itself around, don't
|
|
118
|
+
* clone it.
|
|
114
119
|
*/
|
|
115
|
-
isExpired: boolean;
|
|
120
|
+
readonly isExpired: boolean;
|
|
116
121
|
/**
|
|
117
|
-
* Whether token will expire soon (within refresh window)
|
|
122
|
+
* Whether token will expire soon (within refresh window). Dynamic getter —
|
|
123
|
+
* same spread/JSON caveat as `isExpired`.
|
|
118
124
|
*/
|
|
119
|
-
needsRefresh: boolean;
|
|
125
|
+
readonly needsRefresh: boolean;
|
|
120
126
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HttpClient } from '@angular/common/http';
|
|
1
|
+
import { HttpBackend, HttpClient } from '@angular/common/http';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import { MedHassoConfig, TokenInfo, MedHassoUser } from '../interfaces/medhasso-config.interface';
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
@@ -9,6 +9,15 @@ export declare class TokenManagementService {
|
|
|
9
9
|
private refreshInProgress$;
|
|
10
10
|
private tokenSubject;
|
|
11
11
|
private userSubject;
|
|
12
|
+
/**
|
|
13
|
+
* Dedicated HttpClient wired directly to HttpBackend so the refresh POST
|
|
14
|
+
* bypasses the HTTP_INTERCEPTORS chain. Routing refresh through the normal
|
|
15
|
+
* http (which runs MedHassoAuthInterceptor) causes self-interception:
|
|
16
|
+
* interceptor → ensureValidToken → refreshToken → returns the in-flight
|
|
17
|
+
* refreshInProgress$ which is itself → deadlock, with finalize never firing
|
|
18
|
+
* so refreshInProgress$ stays pinned and every subsequent request hangs too.
|
|
19
|
+
*/
|
|
20
|
+
private readonly refreshHttp;
|
|
12
21
|
private readonly onVisibilityChange;
|
|
13
22
|
private readonly onWindowFocus;
|
|
14
23
|
token$: Observable<TokenInfo | null>;
|
|
@@ -19,7 +28,7 @@ export declare class TokenManagementService {
|
|
|
19
28
|
private get refreshTokenKey();
|
|
20
29
|
private get refreshBeforeExpiryMinutes();
|
|
21
30
|
private get autoRefresh();
|
|
22
|
-
constructor(config: MedHassoConfig, http: HttpClient);
|
|
31
|
+
constructor(config: MedHassoConfig, http: HttpClient, httpBackend: HttpBackend);
|
|
23
32
|
/**
|
|
24
33
|
* Re-validate the token whenever the tab regains focus/visibility.
|
|
25
34
|
* The refresh timer is a JS timer and does not fire while the OS/browser
|
|
@@ -58,7 +67,15 @@ export declare class TokenManagementService {
|
|
|
58
67
|
*/
|
|
59
68
|
private clearStoredTokens;
|
|
60
69
|
/**
|
|
61
|
-
* Create token info object from JWT token
|
|
70
|
+
* Create token info object from JWT token.
|
|
71
|
+
*
|
|
72
|
+
* `isExpired` and `needsRefresh` are getters, not snapshotted booleans:
|
|
73
|
+
* the TokenInfo object may outlive wall-clock events that change its
|
|
74
|
+
* expiry status (e.g. OS/browser suspended overnight, laptop sleep).
|
|
75
|
+
* A snapshotted boolean captured at creation time would then lie — the
|
|
76
|
+
* wake handler would call ensureValidToken(), see `isExpired: false`
|
|
77
|
+
* from the pre-sleep snapshot, and skip the refresh. Reading these as
|
|
78
|
+
* getters means every access reflects the current clock.
|
|
62
79
|
*/
|
|
63
80
|
private createTokenInfo;
|
|
64
81
|
/**
|