@memberjunction/ng-auth-services 4.0.0 → 4.1.0
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 +99 -305
- package/package.json +3 -3
- package/dist/lib/mjexplorer-auth0-provider.service.d.ts +0 -18
- package/dist/lib/mjexplorer-auth0-provider.service.d.ts.map +0 -1
- package/dist/lib/mjexplorer-auth0-provider.service.js +0 -45
- package/dist/lib/mjexplorer-auth0-provider.service.js.map +0 -1
- package/dist/lib/mjexplorer-msal-provider.service.d.ts +0 -24
- package/dist/lib/mjexplorer-msal-provider.service.d.ts.map +0 -1
- package/dist/lib/mjexplorer-msal-provider.service.js +0 -108
- package/dist/lib/mjexplorer-msal-provider.service.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
# @memberjunction/ng-auth-services
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Angular authentication services for MemberJunction Explorer applications. Provides a unified abstraction layer supporting multiple OAuth providers (Auth0, Microsoft MSAL, Okta) through a consistent, type-safe API.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
This package implements a provider pattern
|
|
7
|
+
This package implements a provider-agnostic authentication system using the Strategy pattern. A single abstract base class (`MJAuthBase`) defines the contract, while concrete providers handle Auth0, MSAL (Azure AD), and Okta specifics. Application code interacts only with the standardized interface, making provider switches a configuration change rather than a code change.
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
graph TD
|
|
11
|
+
A["MJAuthBase\n(Abstract Service)"] --> B["MJAuth0Provider"]
|
|
12
|
+
A --> C["MJMSALProvider"]
|
|
13
|
+
A --> D["MJOktaProvider"]
|
|
14
|
+
B --> E["@auth0/auth0-angular"]
|
|
15
|
+
C --> F["@azure/msal-angular"]
|
|
16
|
+
D --> G["@okta/okta-auth-js"]
|
|
17
|
+
|
|
18
|
+
style A fill:#7c5295,stroke:#563a6b,color:#fff
|
|
19
|
+
style B fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
20
|
+
style C fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
21
|
+
style D fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
22
|
+
style E fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
23
|
+
style F fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
24
|
+
style G fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
25
|
+
```
|
|
8
26
|
|
|
9
27
|
## Features
|
|
10
28
|
|
|
11
|
-
- **Unified Authentication Interface**:
|
|
12
|
-
- **Multiple Provider Support**:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **TypeScript Support**: Full TypeScript definitions for type safety
|
|
29
|
+
- **Unified Authentication Interface**: Standardized API across all providers via `IAngularAuthProvider`
|
|
30
|
+
- **Multiple Provider Support**: Auth0, Microsoft MSAL (Azure AD), and Okta
|
|
31
|
+
- **Standardized Types (v3.0.0)**: `StandardUserInfo`, `StandardAuthToken`, `StandardAuthError` eliminate leaky abstractions
|
|
32
|
+
- **Semantic Error Classification**: `AuthErrorType` enum replaces provider-specific error checking
|
|
33
|
+
- **Token Management**: ID token retrieval, token info, and automatic refresh
|
|
34
|
+
- **Observable State**: Reactive authentication state, user info, and email streams
|
|
35
|
+
- **Configuration Validation**: Each provider declares and validates its required configuration
|
|
19
36
|
- **Angular 21+ Compatible**: Built for modern Angular applications
|
|
20
37
|
|
|
21
38
|
## Installation
|
|
@@ -24,355 +41,132 @@ This package implements a provider pattern that allows seamless switching betwee
|
|
|
24
41
|
npm install @memberjunction/ng-auth-services
|
|
25
42
|
```
|
|
26
43
|
|
|
27
|
-
##
|
|
28
|
-
|
|
29
|
-
### Peer Dependencies
|
|
30
|
-
- `@angular/common`: ^18.0.2
|
|
31
|
-
- `@angular/core`: ^18.0.2
|
|
32
|
-
- `@angular/forms`: ^18.0.2
|
|
33
|
-
- `@angular/router`: ^18.0.2
|
|
34
|
-
- `@auth0/auth0-angular`: ^2.2.1 (required when using Auth0)
|
|
35
|
-
- `@azure/msal-angular`: ^3.0.11 (required when using MSAL)
|
|
44
|
+
## Key Dependencies
|
|
36
45
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
| Dependency | Purpose |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `@angular/core`, `@angular/common` | Angular framework |
|
|
49
|
+
| `@auth0/auth0-angular` | Auth0 provider (peer) |
|
|
50
|
+
| `@azure/msal-angular`, `@azure/msal-browser` | MSAL provider (peer) |
|
|
51
|
+
| `@okta/okta-auth-js` | Okta provider (peer) |
|
|
52
|
+
| `@memberjunction/core` | Core MJ utilities |
|
|
53
|
+
| `rxjs` | Reactive state management |
|
|
40
54
|
|
|
41
55
|
## Configuration
|
|
42
56
|
|
|
43
57
|
### Environment Setup
|
|
44
58
|
|
|
45
|
-
Configure your authentication provider in your environment files:
|
|
46
|
-
|
|
47
|
-
#### Auth0 Configuration
|
|
48
59
|
```typescript
|
|
49
|
-
//
|
|
60
|
+
// Auth0
|
|
50
61
|
export const environment = {
|
|
51
62
|
AUTH_TYPE: 'auth0',
|
|
52
63
|
AUTH0_DOMAIN: 'your-domain.auth0.com',
|
|
53
64
|
AUTH0_CLIENTID: 'your-auth0-client-id',
|
|
54
|
-
// Other environment variables...
|
|
55
65
|
};
|
|
56
|
-
```
|
|
57
66
|
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
// environment.ts
|
|
67
|
+
// MSAL (Azure AD)
|
|
61
68
|
export const environment = {
|
|
62
69
|
AUTH_TYPE: 'msal',
|
|
63
70
|
CLIENT_ID: 'your-azure-ad-client-id',
|
|
64
71
|
CLIENT_AUTHORITY: 'https://login.microsoftonline.com/your-tenant-id',
|
|
65
|
-
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Okta
|
|
75
|
+
export const environment = {
|
|
76
|
+
AUTH_TYPE: 'okta',
|
|
77
|
+
OKTA_ISSUER: 'https://your-org.okta.com/oauth2/default',
|
|
78
|
+
OKTA_CLIENTID: 'your-okta-client-id',
|
|
66
79
|
};
|
|
67
80
|
```
|
|
68
81
|
|
|
69
82
|
### Module Setup
|
|
70
83
|
|
|
71
|
-
Import and configure the `AuthServicesModule` in your app module:
|
|
72
|
-
|
|
73
84
|
```typescript
|
|
74
|
-
import { NgModule } from '@angular/core';
|
|
75
|
-
import { BrowserModule } from '@angular/platform-browser';
|
|
76
85
|
import { AuthServicesModule } from '@memberjunction/ng-auth-services';
|
|
77
|
-
import { environment } from '../environments/environment';
|
|
78
|
-
import { AppComponent } from './app.component';
|
|
79
86
|
|
|
80
87
|
@NgModule({
|
|
81
|
-
declarations: [
|
|
82
|
-
AppComponent
|
|
83
|
-
],
|
|
84
88
|
imports: [
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// Other imports...
|
|
88
|
-
],
|
|
89
|
-
providers: [],
|
|
90
|
-
bootstrap: [AppComponent]
|
|
89
|
+
AuthServicesModule.forRoot(environment),
|
|
90
|
+
]
|
|
91
91
|
})
|
|
92
|
-
export class AppModule {
|
|
92
|
+
export class AppModule {}
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
When using MSAL, add the redirect component to your routes:
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
import { Routes } from '@angular/router';
|
|
101
|
-
import { RedirectComponent } from '@memberjunction/ng-auth-services';
|
|
102
|
-
|
|
103
|
-
const routes: Routes = [
|
|
104
|
-
// Your application routes...
|
|
105
|
-
{ path: 'auth', component: RedirectComponent } // Required for MSAL
|
|
106
|
-
];
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Usage Examples
|
|
110
|
-
|
|
111
|
-
### Basic Authentication Operations
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
import { Component, OnInit } from '@angular/core';
|
|
115
|
-
import { MJAuthBase } from '@memberjunction/ng-auth-services';
|
|
116
|
-
import { Observable } from 'rxjs';
|
|
117
|
-
|
|
118
|
-
@Component({
|
|
119
|
-
selector: 'app-header',
|
|
120
|
-
template: `
|
|
121
|
-
<div class="header">
|
|
122
|
-
<button *ngIf="!(isAuthenticated$ | async)" (click)="login()">Login</button>
|
|
123
|
-
<button *ngIf="isAuthenticated$ | async" (click)="logout()">Logout</button>
|
|
124
|
-
<span *ngIf="user$ | async as user">Welcome, {{ user.name }}!</span>
|
|
125
|
-
</div>
|
|
126
|
-
`
|
|
127
|
-
})
|
|
128
|
-
export class HeaderComponent implements OnInit {
|
|
129
|
-
isAuthenticated$!: Observable<boolean>;
|
|
130
|
-
user$!: Observable<any>;
|
|
131
|
-
|
|
132
|
-
constructor(private authService: MJAuthBase) {}
|
|
133
|
-
|
|
134
|
-
async ngOnInit() {
|
|
135
|
-
this.isAuthenticated$ = await this.authService.isAuthenticated();
|
|
136
|
-
this.user$ = await this.authService.getUser();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
login() {
|
|
140
|
-
this.authService.login();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
logout() {
|
|
144
|
-
this.authService.logout();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
```
|
|
95
|
+
## Usage
|
|
148
96
|
|
|
149
|
-
###
|
|
97
|
+
### Authentication Operations (v3.0.0 API)
|
|
150
98
|
|
|
151
99
|
```typescript
|
|
152
|
-
import {
|
|
153
|
-
import { MJAuthBase } from '@memberjunction/ng-auth-services';
|
|
154
|
-
import { Observable } from 'rxjs';
|
|
155
|
-
import { map } from 'rxjs/operators';
|
|
156
|
-
|
|
157
|
-
@Component({
|
|
158
|
-
selector: 'app-user-profile',
|
|
159
|
-
template: `
|
|
160
|
-
<div *ngIf="userClaims$ | async as claims">
|
|
161
|
-
<h3>User Profile</h3>
|
|
162
|
-
<p>Email: {{ claims.email }}</p>
|
|
163
|
-
<p>Name: {{ claims.name }}</p>
|
|
164
|
-
<p>Roles: {{ claims.roles?.join(', ') }}</p>
|
|
165
|
-
</div>
|
|
166
|
-
`
|
|
167
|
-
})
|
|
168
|
-
export class UserProfileComponent implements OnInit {
|
|
169
|
-
userClaims$!: Observable<any>;
|
|
100
|
+
import { MJAuthBase, StandardUserInfo } from '@memberjunction/ng-auth-services';
|
|
170
101
|
|
|
171
|
-
|
|
102
|
+
@Component({ selector: 'app-header', template: '...' })
|
|
103
|
+
export class HeaderComponent {
|
|
104
|
+
constructor(private authBase: MJAuthBase) {}
|
|
172
105
|
|
|
173
106
|
async ngOnInit() {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
```
|
|
107
|
+
// Reactive auth state
|
|
108
|
+
this.authBase.isAuthenticated().subscribe(isAuth => { /* ... */ });
|
|
178
109
|
|
|
179
|
-
|
|
110
|
+
// Standardized user info
|
|
111
|
+
this.authBase.getUserInfo().subscribe((user: StandardUserInfo | null) => {
|
|
112
|
+
if (user) console.log(`Welcome ${user.name}!`);
|
|
113
|
+
});
|
|
180
114
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
import { MJAuthBase } from '@memberjunction/ng-auth-services';
|
|
185
|
-
import { catchError, switchMap } from 'rxjs/operators';
|
|
186
|
-
import { throwError } from 'rxjs';
|
|
187
|
-
|
|
188
|
-
@Injectable({
|
|
189
|
-
providedIn: 'root'
|
|
190
|
-
})
|
|
191
|
-
export class ApiService {
|
|
192
|
-
constructor(
|
|
193
|
-
private http: HttpClient,
|
|
194
|
-
private authService: MJAuthBase
|
|
195
|
-
) {}
|
|
196
|
-
|
|
197
|
-
getData() {
|
|
198
|
-
return this.http.get('/api/data').pipe(
|
|
199
|
-
catchError((error: HttpErrorResponse) => this.handleError(error))
|
|
200
|
-
);
|
|
115
|
+
// Token management
|
|
116
|
+
const token = await this.authBase.getIdToken();
|
|
117
|
+
const tokenInfo = await this.authBase.getTokenInfo();
|
|
201
118
|
}
|
|
202
119
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
// Token expired, try to refresh
|
|
206
|
-
const tokenObs = await this.authService.refresh();
|
|
207
|
-
return tokenObs.pipe(
|
|
208
|
-
switchMap(() => this.http.get('/api/data')) // Retry the request
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
return throwError(() => error);
|
|
212
|
-
}
|
|
120
|
+
login() { this.authBase.login(); }
|
|
121
|
+
logout() { this.authBase.logout(); }
|
|
213
122
|
}
|
|
214
123
|
```
|
|
215
124
|
|
|
216
|
-
###
|
|
125
|
+
### Error Handling
|
|
217
126
|
|
|
218
127
|
```typescript
|
|
219
|
-
import {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return isAuthenticated$.pipe(
|
|
236
|
-
tap(authenticated => {
|
|
237
|
-
if (!authenticated) {
|
|
238
|
-
this.authService.login();
|
|
239
|
-
}
|
|
240
|
-
})
|
|
241
|
-
);
|
|
128
|
+
import { AuthErrorType } from '@memberjunction/ng-auth-services';
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
await this.authBase.refreshToken();
|
|
132
|
+
} catch (err) {
|
|
133
|
+
const authError = this.authBase.classifyError(err);
|
|
134
|
+
switch (authError.type) {
|
|
135
|
+
case AuthErrorType.TOKEN_EXPIRED:
|
|
136
|
+
// Session expired
|
|
137
|
+
break;
|
|
138
|
+
case AuthErrorType.USER_CANCELLED:
|
|
139
|
+
// User cancelled - no error needed
|
|
140
|
+
break;
|
|
141
|
+
case AuthErrorType.NETWORK_ERROR:
|
|
142
|
+
// Connectivity issue
|
|
143
|
+
break;
|
|
242
144
|
}
|
|
243
145
|
}
|
|
244
146
|
```
|
|
245
147
|
|
|
246
|
-
## API
|
|
148
|
+
## Exported API
|
|
247
149
|
|
|
248
|
-
|
|
150
|
+
| Export | Type | Description |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| `MJAuthBase` | Abstract Service | Base authentication service all providers implement |
|
|
153
|
+
| `IAngularAuthProvider` | Interface | Contract for authentication providers |
|
|
154
|
+
| `AngularAuthProviderFactory` | Factory | Creates provider instances from configuration |
|
|
155
|
+
| `AuthServicesModule` | NgModule | Module with `forRoot()` configuration |
|
|
156
|
+
| `RedirectComponent` | Component | Handles OAuth redirect callbacks |
|
|
157
|
+
| `StandardUserInfo` | Interface | Standardized user profile data |
|
|
158
|
+
| `StandardAuthToken` | Interface | Standardized token information |
|
|
159
|
+
| `StandardAuthError` | Interface | Standardized error with classification |
|
|
160
|
+
| `AuthErrorType` | Enum | Semantic error categories |
|
|
161
|
+
| `TokenRefreshResult` | Interface | Token refresh operation result |
|
|
162
|
+
| `AuthState` | Interface | Complete authentication state snapshot |
|
|
249
163
|
|
|
250
|
-
|
|
164
|
+
## Build
|
|
251
165
|
|
|
252
|
-
#### Properties
|
|
253
|
-
|
|
254
|
-
| Property | Type | Description |
|
|
255
|
-
|----------|------|-------------|
|
|
256
|
-
| `authenticated` | `boolean` | Current authentication state |
|
|
257
|
-
|
|
258
|
-
#### Methods
|
|
259
|
-
|
|
260
|
-
| Method | Parameters | Return Type | Description |
|
|
261
|
-
|--------|------------|-------------|-------------|
|
|
262
|
-
| `login` | `options?: any` | `Promise<any>` | Initiates the login flow. Options vary by provider. |
|
|
263
|
-
| `logout` | None | `Promise<any>` | Logs the user out and clears authentication state |
|
|
264
|
-
| `refresh` | None | `Promise<Observable<any>>` | Refreshes the authentication token |
|
|
265
|
-
| `isAuthenticated` | None | `Promise<any>` | Returns an Observable of the authentication state |
|
|
266
|
-
| `getUser` | None | `Promise<any>` | Returns user information (format varies by provider) |
|
|
267
|
-
| `getUserClaims` | None | `Promise<Observable<any>>` | Returns the user's token claims |
|
|
268
|
-
| `checkExpiredTokenError` | `error: string` | `boolean` | Checks if an error indicates an expired token |
|
|
269
|
-
|
|
270
|
-
### MJAuth0Provider
|
|
271
|
-
|
|
272
|
-
Auth0-specific implementation of `MJAuthBase`. Internally uses `@auth0/auth0-angular`.
|
|
273
|
-
|
|
274
|
-
#### Provider-Specific Behavior
|
|
275
|
-
- Uses Auth0's redirect flow for authentication
|
|
276
|
-
- Returns Auth0 `User` object from `getUser()`
|
|
277
|
-
- Token expiration check looks for "jwt expired" in error messages
|
|
278
|
-
|
|
279
|
-
### MJMSALProvider
|
|
280
|
-
|
|
281
|
-
MSAL-specific implementation of `MJAuthBase`. Internally uses `@azure/msal-angular`.
|
|
282
|
-
|
|
283
|
-
#### Provider-Specific Behavior
|
|
284
|
-
- Implements initialization handling to ensure MSAL is ready
|
|
285
|
-
- Uses redirect flow with automatic account selection
|
|
286
|
-
- Returns MSAL `AccountInfo` from `getUser()`
|
|
287
|
-
- Includes refresh token policy in token operations
|
|
288
|
-
- Token expiration check looks for authorization errors
|
|
289
|
-
|
|
290
|
-
### AuthServicesModule
|
|
291
|
-
|
|
292
|
-
The main module for configuring authentication services.
|
|
293
|
-
|
|
294
|
-
#### Static Methods
|
|
295
|
-
|
|
296
|
-
| Method | Parameters | Return Type | Description |
|
|
297
|
-
|--------|------------|-------------|-------------|
|
|
298
|
-
| `forRoot` | `environment: AuthEnvironment` | `ModuleWithProviders<AuthServicesModule>` | Configures the authentication module with environment settings |
|
|
299
|
-
|
|
300
|
-
#### AuthEnvironment Type
|
|
301
|
-
|
|
302
|
-
```typescript
|
|
303
|
-
type AuthEnvironment = {
|
|
304
|
-
AUTH_TYPE: string; // 'auth0' or 'msal'
|
|
305
|
-
CLIENT_ID: string; // MSAL client ID
|
|
306
|
-
CLIENT_AUTHORITY: string; // MSAL authority URL
|
|
307
|
-
AUTH0_CLIENTID: string; // Auth0 client ID
|
|
308
|
-
AUTH0_DOMAIN: string; // Auth0 domain
|
|
309
|
-
};
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### RedirectComponent
|
|
313
|
-
|
|
314
|
-
Re-exported from `@azure/msal-angular`. Required for MSAL redirect flow handling.
|
|
315
|
-
|
|
316
|
-
## Integration with Other MemberJunction Packages
|
|
317
|
-
|
|
318
|
-
This package integrates with:
|
|
319
|
-
- **@memberjunction/core**: Uses core logging utilities (`LogError`)
|
|
320
|
-
- **MemberJunction Explorer**: Provides authentication for all Explorer UI components
|
|
321
|
-
- **MemberJunction API**: Token management for API authentication
|
|
322
|
-
|
|
323
|
-
## Build and Development
|
|
324
|
-
|
|
325
|
-
### Building the Package
|
|
326
166
|
```bash
|
|
327
|
-
|
|
328
|
-
npm run build
|
|
329
|
-
|
|
330
|
-
# From the repository root
|
|
331
|
-
npm run build -- --filter="@memberjunction/ng-auth-services"
|
|
167
|
+
cd packages/Angular/Explorer/auth-services && npm run build
|
|
332
168
|
```
|
|
333
169
|
|
|
334
|
-
### Development Notes
|
|
335
|
-
- The package uses Angular Package Format (APF)
|
|
336
|
-
- Compiled with Angular Compiler (`ngc`)
|
|
337
|
-
- No side effects - tree-shakeable
|
|
338
|
-
- Distributed files are in the `/dist` directory
|
|
339
|
-
|
|
340
|
-
## Migration Guide
|
|
341
|
-
|
|
342
|
-
### Switching Between Providers
|
|
343
|
-
|
|
344
|
-
To switch authentication providers:
|
|
345
|
-
|
|
346
|
-
1. Update your environment configuration:
|
|
347
|
-
```typescript
|
|
348
|
-
// From Auth0 to MSAL
|
|
349
|
-
AUTH_TYPE: 'msal', // was 'auth0'
|
|
350
|
-
CLIENT_ID: 'your-azure-client-id',
|
|
351
|
-
CLIENT_AUTHORITY: 'https://login.microsoftonline.com/your-tenant',
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
2. Add redirect route (if switching to MSAL):
|
|
355
|
-
```typescript
|
|
356
|
-
{ path: 'auth', component: RedirectComponent }
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
3. No other code changes required - the `MJAuthBase` interface remains the same
|
|
360
|
-
|
|
361
|
-
## Troubleshooting
|
|
362
|
-
|
|
363
|
-
### Common Issues
|
|
364
|
-
|
|
365
|
-
1. **MSAL Initialization Errors**: Ensure the MSAL redirect component is properly configured in routes
|
|
366
|
-
2. **Token Refresh Failures**: Check that refresh token is enabled in your auth provider configuration
|
|
367
|
-
3. **CORS Issues**: Verify redirect URIs are properly configured in your auth provider dashboard
|
|
368
|
-
|
|
369
|
-
### Debug Tips
|
|
370
|
-
|
|
371
|
-
- Check browser console for authentication errors
|
|
372
|
-
- Verify localStorage contains auth tokens
|
|
373
|
-
- Use browser dev tools to inspect network requests for auth headers
|
|
374
|
-
- Enable verbose logging in your auth provider configuration
|
|
375
|
-
|
|
376
170
|
## License
|
|
377
171
|
|
|
378
|
-
ISC
|
|
172
|
+
ISC
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-auth-services",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "MemberJunction Explorer: Authentication Services",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"@okta/okta-auth-js": "^7.14.1"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@memberjunction/core": "4.
|
|
34
|
-
"@memberjunction/global": "4.
|
|
33
|
+
"@memberjunction/core": "4.1.0",
|
|
34
|
+
"@memberjunction/global": "4.1.0",
|
|
35
35
|
"rxjs": "^7.8.2",
|
|
36
36
|
"tslib": "^2.8.1"
|
|
37
37
|
},
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { MJAuthBase } from './mjexplorer-auth-base.service';
|
|
2
|
-
import { AuthService, User } from '@auth0/auth0-angular';
|
|
3
|
-
import { Observable } from 'rxjs';
|
|
4
|
-
import * as i0 from "@angular/core";
|
|
5
|
-
export declare class MJAuth0Provider extends MJAuthBase {
|
|
6
|
-
auth: AuthService;
|
|
7
|
-
constructor(auth: AuthService);
|
|
8
|
-
login(options?: any): any;
|
|
9
|
-
logout(): Promise<void>;
|
|
10
|
-
refresh(): Promise<Observable<any>>;
|
|
11
|
-
getUser(): Promise<Observable<User | null | undefined>>;
|
|
12
|
-
isAuthenticated(): Promise<Observable<boolean>>;
|
|
13
|
-
getUserClaims(): Promise<Observable<any>>;
|
|
14
|
-
checkExpiredTokenError(error: string): boolean;
|
|
15
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<MJAuth0Provider, never>;
|
|
16
|
-
static ɵprov: i0.ɵɵInjectableDeclaration<MJAuth0Provider>;
|
|
17
|
-
}
|
|
18
|
-
//# sourceMappingURL=mjexplorer-auth0-provider.service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mjexplorer-auth0-provider.service.d.ts","sourceRoot":"","sources":["../../src/lib/mjexplorer-auth0-provider.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAW,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAM,MAAM,MAAM,CAAC;;AAEtC,qBAGa,eAAgB,SAAQ,UAAU;IAE1B,IAAI,EAAE,WAAW;gBAAjB,IAAI,EAAE,WAAW;IAO3B,KAAK,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,GAAG;IAIrB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvB,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAK1C,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAIvD,eAAe;IAIf,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAI/C,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;yCAlCnC,eAAe;6CAAf,eAAe;CAsC3B"}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { Injectable } from '@angular/core';
|
|
2
|
-
import { MJAuthBase } from './mjexplorer-auth-base.service';
|
|
3
|
-
import * as i0 from "@angular/core";
|
|
4
|
-
import * as i1 from "@auth0/auth0-angular";
|
|
5
|
-
export class MJAuth0Provider extends MJAuthBase {
|
|
6
|
-
auth;
|
|
7
|
-
constructor(auth) {
|
|
8
|
-
super();
|
|
9
|
-
this.auth = auth;
|
|
10
|
-
this.auth.isAuthenticated$.subscribe((loggedIn) => {
|
|
11
|
-
this.setAuthenticated(loggedIn);
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
login(options) {
|
|
15
|
-
this.auth.loginWithRedirect(options);
|
|
16
|
-
}
|
|
17
|
-
async logout() {
|
|
18
|
-
this.auth.logout({ logoutParams: { returnTo: document.location.origin } });
|
|
19
|
-
}
|
|
20
|
-
async refresh() {
|
|
21
|
-
await this.auth.getAccessTokenSilently();
|
|
22
|
-
return this.auth.idTokenClaims$;
|
|
23
|
-
}
|
|
24
|
-
async getUser() {
|
|
25
|
-
return this.auth.user$;
|
|
26
|
-
}
|
|
27
|
-
async isAuthenticated() {
|
|
28
|
-
return this.auth.isAuthenticated$;
|
|
29
|
-
}
|
|
30
|
-
async getUserClaims() {
|
|
31
|
-
return this.auth.idTokenClaims$;
|
|
32
|
-
}
|
|
33
|
-
checkExpiredTokenError(error) {
|
|
34
|
-
return error?.includes('jwt expired');
|
|
35
|
-
}
|
|
36
|
-
static ɵfac = function MJAuth0Provider_Factory(t) { return new (t || MJAuth0Provider)(i0.ɵɵinject(i1.AuthService)); };
|
|
37
|
-
static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: MJAuth0Provider, factory: MJAuth0Provider.ɵfac, providedIn: 'root' });
|
|
38
|
-
}
|
|
39
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJAuth0Provider, [{
|
|
40
|
-
type: Injectable,
|
|
41
|
-
args: [{
|
|
42
|
-
providedIn: 'root'
|
|
43
|
-
}]
|
|
44
|
-
}], () => [{ type: i1.AuthService }], null); })();
|
|
45
|
-
//# sourceMappingURL=mjexplorer-auth0-provider.service.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mjexplorer-auth0-provider.service.js","sourceRoot":"","sources":["../../src/lib/mjexplorer-auth0-provider.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;;;AAO5D,MAAM,OAAO,eAAgB,SAAQ,UAAU;IAE1B;IAAnB,YAAmB,IAAiB;QAClC,KAAK,EAAE,CAAC;QADS,SAAI,GAAJ,IAAI,CAAa;QAElC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,EAAE;YAChD,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACL,CAAC;IAEQ,KAAK,CAAC,OAAa;QAC1B,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAEM,KAAK,CAAC,MAAM;QACjB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC;IAEM,KAAK,CAAC,OAAO;QAClB,MAAM,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,OAAO;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,OAAO,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;IAClC,CAAC;IAED,sBAAsB,CAAC,KAAa;QAClC,OAAO,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;IACxC,CAAC;yEApCU,eAAe;gEAAf,eAAe,WAAf,eAAe,mBAFd,MAAM;;iFAEP,eAAe;cAH3B,UAAU;eAAC;gBACV,UAAU,EAAE,MAAM;aACnB"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { MJAuthBase } from './mjexplorer-auth-base.service';
|
|
2
|
-
import { Observable } from 'rxjs';
|
|
3
|
-
import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
|
|
4
|
-
import { AccountInfo } from '@azure/msal-common';
|
|
5
|
-
import * as i0 from "@angular/core";
|
|
6
|
-
export declare class MJMSALProvider extends MJAuthBase {
|
|
7
|
-
auth: MsalService;
|
|
8
|
-
private msalBroadcastService;
|
|
9
|
-
private readonly _destroying$;
|
|
10
|
-
private readonly _initializationCompleted$;
|
|
11
|
-
constructor(auth: MsalService, msalBroadcastService: MsalBroadcastService);
|
|
12
|
-
private initializeMSAL;
|
|
13
|
-
private ensureInitialized;
|
|
14
|
-
login(options?: any): Promise<any>;
|
|
15
|
-
logout(): Promise<void>;
|
|
16
|
-
refresh(): Promise<Observable<any>>;
|
|
17
|
-
getUser(): Promise<AccountInfo | null>;
|
|
18
|
-
isAuthenticated(): Promise<Observable<boolean>>;
|
|
19
|
-
getUserClaims(): Promise<Observable<any>>;
|
|
20
|
-
checkExpiredTokenError(error: string): boolean;
|
|
21
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<MJMSALProvider, never>;
|
|
22
|
-
static ɵprov: i0.ɵɵInjectableDeclaration<MJMSALProvider>;
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=mjexplorer-msal-provider.service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mjexplorer-msal-provider.service.d.ts","sourceRoot":"","sources":["../../src/lib/mjexplorer-msal-provider.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAmB,UAAU,EAA2F,MAAM,MAAM,CAAC;AAC5I,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAwB,MAAM,oBAAoB,CAAC;;AAIvE,qBAGa,cAAe,SAAQ,UAAU;IAKzB,IAAI,EAAE,WAAW;IAAE,OAAO,CAAC,oBAAoB;IAHlE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuB;IACpD,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAuC;gBAE9D,IAAI,EAAE,WAAW,EAAU,oBAAoB,EAAE,oBAAoB;YAK1E,cAAc;YAcd,iBAAiB;IAMhB,KAAK,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAgBpC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAOvB,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAS1C,OAAO,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAKtC,eAAe;IAKf,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAwB/C,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;yCAhGnC,cAAc;6CAAd,cAAc;CAoG1B"}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { Injectable } from '@angular/core';
|
|
2
|
-
import { MJAuthBase } from './mjexplorer-auth-base.service';
|
|
3
|
-
import { BehaviorSubject, Subject, catchError, filter, from, map, of, throwError, takeUntil, take, firstValueFrom } from 'rxjs';
|
|
4
|
-
import { CacheLookupPolicy, InteractionRequiredAuthError, InteractionStatus } from '@azure/msal-browser';
|
|
5
|
-
import { LogError } from '@memberjunction/core';
|
|
6
|
-
import * as i0 from "@angular/core";
|
|
7
|
-
import * as i1 from "@azure/msal-angular";
|
|
8
|
-
export class MJMSALProvider extends MJAuthBase {
|
|
9
|
-
auth;
|
|
10
|
-
msalBroadcastService;
|
|
11
|
-
_destroying$ = new Subject();
|
|
12
|
-
_initializationCompleted$ = new BehaviorSubject(false);
|
|
13
|
-
constructor(auth, msalBroadcastService) {
|
|
14
|
-
super();
|
|
15
|
-
this.auth = auth;
|
|
16
|
-
this.msalBroadcastService = msalBroadcastService;
|
|
17
|
-
this.initializeMSAL();
|
|
18
|
-
}
|
|
19
|
-
async initializeMSAL() {
|
|
20
|
-
await this.auth.instance.initialize();
|
|
21
|
-
// After initialization logic
|
|
22
|
-
this.auth.instance.setActiveAccount(this.auth.instance.getAllAccounts()[0] || null);
|
|
23
|
-
this.msalBroadcastService.inProgress$
|
|
24
|
-
.pipe(filter((status) => status === InteractionStatus.None), takeUntil(this._destroying$))
|
|
25
|
-
.subscribe(() => {
|
|
26
|
-
this.setAuthenticated(this.auth.instance.getAllAccounts().length > 0);
|
|
27
|
-
this.auth.instance.setActiveAccount(this.auth.instance.getAllAccounts()[0] || null);
|
|
28
|
-
this._initializationCompleted$.next(true); // Signal initialization complete
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
// Ensure methods wait for initialization
|
|
32
|
-
async ensureInitialized() {
|
|
33
|
-
if (!this._initializationCompleted$.value) {
|
|
34
|
-
await firstValueFrom(this._initializationCompleted$.pipe(filter(done => done), take(1)));
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
async login(options) {
|
|
38
|
-
await this.ensureInitialized();
|
|
39
|
-
const silentRequest = {
|
|
40
|
-
scopes: ['User.Read', 'email', 'profile']
|
|
41
|
-
};
|
|
42
|
-
this.auth.loginRedirect(silentRequest).subscribe({
|
|
43
|
-
next: () => {
|
|
44
|
-
this.auth.instance.setActiveAccount(this.auth.instance.getAllAccounts()[0] || null);
|
|
45
|
-
window.location.reload();
|
|
46
|
-
},
|
|
47
|
-
error: (error) => {
|
|
48
|
-
LogError(error);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
async logout() {
|
|
53
|
-
await this.ensureInitialized();
|
|
54
|
-
this.auth.logoutRedirect().subscribe(() => {
|
|
55
|
-
window.location.reload();
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
async refresh() {
|
|
59
|
-
await this.ensureInitialized();
|
|
60
|
-
const silentRequest = {
|
|
61
|
-
scopes: ['User.Read', 'email', 'profile'],
|
|
62
|
-
cacheLookupPolicy: CacheLookupPolicy.RefreshTokenAndNetwork,
|
|
63
|
-
};
|
|
64
|
-
return from(this.auth.instance.acquireTokenSilent(silentRequest));
|
|
65
|
-
}
|
|
66
|
-
async getUser() {
|
|
67
|
-
await this.ensureInitialized();
|
|
68
|
-
return this.auth.instance.getActiveAccount();
|
|
69
|
-
}
|
|
70
|
-
async isAuthenticated() {
|
|
71
|
-
await this.ensureInitialized();
|
|
72
|
-
return of(this.auth.instance.getActiveAccount() != null);
|
|
73
|
-
}
|
|
74
|
-
async getUserClaims() {
|
|
75
|
-
await this.ensureInitialized();
|
|
76
|
-
const account = this.auth.instance.getActiveAccount();
|
|
77
|
-
const silentRequest = {
|
|
78
|
-
scopes: ['User.Read', 'email', 'profile'],
|
|
79
|
-
account: account,
|
|
80
|
-
cacheLookupPolicy: CacheLookupPolicy.RefreshTokenAndNetwork
|
|
81
|
-
};
|
|
82
|
-
if (account) {
|
|
83
|
-
return from(this.auth.instance.acquireTokenSilent(silentRequest)).pipe(map((response) => response), catchError((error) => {
|
|
84
|
-
LogError(error);
|
|
85
|
-
if (error instanceof InteractionRequiredAuthError) {
|
|
86
|
-
return from(this.auth.instance.acquireTokenSilent(silentRequest));
|
|
87
|
-
}
|
|
88
|
-
this.authenticated = false;
|
|
89
|
-
return throwError(error);
|
|
90
|
-
}));
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
return from(this.auth.instance.acquireTokenSilent(silentRequest));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
checkExpiredTokenError(error) {
|
|
97
|
-
return error?.trim().toLowerCase().includes('you need to be authorized to perform');
|
|
98
|
-
}
|
|
99
|
-
static ɵfac = function MJMSALProvider_Factory(t) { return new (t || MJMSALProvider)(i0.ɵɵinject(i1.MsalService), i0.ɵɵinject(i1.MsalBroadcastService)); };
|
|
100
|
-
static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: MJMSALProvider, factory: MJMSALProvider.ɵfac, providedIn: 'root' });
|
|
101
|
-
}
|
|
102
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJMSALProvider, [{
|
|
103
|
-
type: Injectable,
|
|
104
|
-
args: [{
|
|
105
|
-
providedIn: 'root'
|
|
106
|
-
}]
|
|
107
|
-
}], () => [{ type: i1.MsalService }, { type: i1.MsalBroadcastService }], null); })();
|
|
108
|
-
//# sourceMappingURL=mjexplorer-msal-provider.service.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mjexplorer-msal-provider.service.js","sourceRoot":"","sources":["../../src/lib/mjexplorer-msal-provider.service.ts"],"names":[],"mappings":"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"}
|