@eac-arch/infrastructure-security 1.0.2 → 1.0.6
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
CHANGED
|
@@ -1,63 +1,84 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
1
|
+
# @eac-arch/infrastructure-security
|
|
2
|
+
|
|
3
|
+
Angular security library based on OAuth2/OIDC with guards, interceptor, and callback flow.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
`@eac-arch/infrastructure-security` encapsulates authentication and authorization concerns:
|
|
8
|
+
|
|
9
|
+
- OIDC bootstrap via APP_INITIALIZER
|
|
10
|
+
- token lifecycle and profile resolution
|
|
11
|
+
- route guards for authentication and authorization
|
|
12
|
+
- automatic bearer token injection for configured API endpoints
|
|
13
|
+
- callback component/state utilities for code flow redirects
|
|
14
|
+
|
|
15
|
+
## Main Capabilities
|
|
16
|
+
|
|
17
|
+
- `provideSecurity()`
|
|
18
|
+
: Registers OAuth client, `SecurityService`, and startup initializer.
|
|
19
|
+
- `SecurityService`
|
|
20
|
+
: Exposes auth status, token, roles/scopes checks, profile, login/logout.
|
|
21
|
+
- `authGuard`
|
|
22
|
+
: Blocks unauthenticated routes and triggers login flow.
|
|
23
|
+
- `authzGuard`
|
|
24
|
+
: Evaluates route-level role/scope requirements.
|
|
25
|
+
- `authInterceptor`
|
|
26
|
+
: Adds bearer token for URLs configured through `SECURITY.SECURE_API_KEYS` in runtime config.
|
|
27
|
+
- `OidcCallback`
|
|
28
|
+
: Handles post-login callback and safe redirect restore.
|
|
29
|
+
- OAuth state utilities
|
|
30
|
+
: `stateEncode`, `stateDecode`, `stateDecodeFromUrlParam`, `safeRel`.
|
|
31
|
+
|
|
32
|
+
## Public API
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
export type { IdpConfig, UserProfile, SecurityConfig };
|
|
36
|
+
export { SecurityService, provideSecurity };
|
|
37
|
+
export { authGuard, authzGuard, authInterceptor };
|
|
38
|
+
export { OidcCallback };
|
|
39
|
+
export { stateEncode, stateDecode, stateDecodeFromUrlParam, safeRel };
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Runtime Config Requirements
|
|
43
|
+
|
|
44
|
+
Expected config keys include:
|
|
45
|
+
|
|
46
|
+
- `IDP` (issuer, client id, redirect URIs, scopes)
|
|
47
|
+
- `SECURITY.SECURE_API_KEYS` (list of keys that resolve to secure API base URLs)
|
|
48
|
+
|
|
49
|
+
## Dependencies
|
|
50
|
+
|
|
51
|
+
- `@eac-arch/infrastructure-config`
|
|
52
|
+
- `angular-oauth2-oidc`
|
|
53
|
+
- Angular core/common/router
|
|
54
|
+
- `tslib`
|
|
55
|
+
|
|
56
|
+
## Development
|
|
57
|
+
|
|
58
|
+
From `eac-arch-infrastructure-security`:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install
|
|
62
|
+
npm run build
|
|
63
|
+
npm test
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Created By
|
|
68
|
+
|
|
69
|
+
**Erick Arostegui Cunza**
|
|
70
|
+
Enterprise Solutions Architect
|
|
71
|
+
|
|
72
|
+
Professional Profile:
|
|
73
|
+
- LinkedIn: https://www.linkedin.com/in/erick-arostegui-cunza/
|
|
74
|
+
|
|
75
|
+
Articles:
|
|
76
|
+
- Medium: https://medium.com/@scorpius86
|
|
77
|
+
|
|
78
|
+
Channels and Media:
|
|
79
|
+
- Facebook: https://www.facebook.com/Erick.Arostegui.Cunza
|
|
80
|
+
- TikTok: https://www.tiktok.com/@erick_arostegui_cunza
|
|
81
|
+
- Instagram: https://www.instagram.com/erickarosteguicunza/
|
|
82
|
+
- Spotify: https://open.spotify.com/show/2JQxlxcRg7k7cJ1hB52Ge5
|
|
83
|
+
|
|
84
|
+
Created by Erick Arostegui Cunza.
|
|
@@ -1,30 +1,612 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Component } from '@angular/core';
|
|
2
|
+
import { signal, computed, makeEnvironmentProviders, PLATFORM_ID, DestroyRef, APP_INITIALIZER, inject, Component } from '@angular/core';
|
|
3
|
+
import { startWith } from 'rxjs';
|
|
4
|
+
import { OAuthErrorEvent, OAuthService, provideOAuthClient } from 'angular-oauth2-oidc';
|
|
5
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
6
|
+
import { Router } from '@angular/router';
|
|
7
|
+
import { ConfigService } from '@eac-arch/infrastructure-config';
|
|
3
8
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const DEFAULT_SEPARATOR = ';';
|
|
10
|
+
function stateEncode(obj) {
|
|
11
|
+
const s = JSON.stringify(obj);
|
|
12
|
+
return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
|
13
|
+
}
|
|
14
|
+
function stateDecode(s) {
|
|
15
|
+
try {
|
|
16
|
+
const pad = s.length % 4 ? '='.repeat(4 - (s.length % 4)) : '';
|
|
17
|
+
const b64 = s.replace(/-/g, '+').replace(/_/g, '/') + pad;
|
|
18
|
+
return JSON.parse(atob(b64));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function stateDecodeFromUrlParam(s) {
|
|
25
|
+
if (!s)
|
|
26
|
+
return null;
|
|
27
|
+
let decoded = s;
|
|
28
|
+
if (/%[0-9A-Fa-f]{2}/.test(decoded)) {
|
|
29
|
+
try {
|
|
30
|
+
decoded = decodeURIComponent(decoded);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* ignore */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return stateDecode(decoded);
|
|
37
|
+
}
|
|
38
|
+
function safeRel(rel) {
|
|
39
|
+
let r = typeof rel === 'string' ? rel : '/';
|
|
40
|
+
if (!r.startsWith('/'))
|
|
41
|
+
r = '/' + r;
|
|
42
|
+
try {
|
|
43
|
+
const u = new URL(r, window.location.origin);
|
|
44
|
+
if (u.origin !== window.location.origin)
|
|
45
|
+
return '/';
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return '/';
|
|
49
|
+
}
|
|
50
|
+
return r;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Pure functions for extracting roles, scopes and user info from JWT/OIDC claims.
|
|
54
|
+
// Supports both Azure AD and Keycloak claim formats.
|
|
55
|
+
function extractRoles(claims) {
|
|
56
|
+
const out = new Set();
|
|
57
|
+
// Azure AD
|
|
58
|
+
if (Array.isArray(claims['roles']))
|
|
59
|
+
for (const r of claims['roles'])
|
|
60
|
+
out.add(String(r).toLowerCase());
|
|
61
|
+
if (Array.isArray(claims['groups']))
|
|
62
|
+
for (const g of claims['groups'])
|
|
63
|
+
out.add(String(g).toLowerCase());
|
|
64
|
+
// Keycloak
|
|
65
|
+
const realmAccess = claims['realm_access'];
|
|
66
|
+
if (Array.isArray(realmAccess?.roles))
|
|
67
|
+
for (const r of realmAccess.roles)
|
|
68
|
+
out.add(String(r).toLowerCase());
|
|
69
|
+
const ra = claims['resource_access'];
|
|
70
|
+
if (ra)
|
|
71
|
+
for (const k of Object.keys(ra))
|
|
72
|
+
ra[k]?.roles?.forEach((r) => out.add(String(r).toLowerCase()));
|
|
73
|
+
return Array.from(out);
|
|
74
|
+
}
|
|
75
|
+
function extractScopes(claims) {
|
|
76
|
+
// Azure AD uses 'scp', Keycloak uses 'scope'
|
|
77
|
+
const raw = (claims['scp'] ?? claims['scope']);
|
|
78
|
+
if (typeof raw !== 'string' || !raw)
|
|
79
|
+
return [];
|
|
80
|
+
return raw.trim().split(/\s+/).map((s) => s.toLowerCase());
|
|
81
|
+
}
|
|
82
|
+
function mapClaimsToUserinfo(claims) {
|
|
83
|
+
const first = (claims['given_name'] ?? claims['givenName'] ?? claims['firstName'] ?? null);
|
|
84
|
+
const last = (claims['family_name'] ?? claims['familyName'] ?? claims['lastName'] ?? null);
|
|
85
|
+
const display = (claims['name'] ?? [first, last].filter(Boolean).join(' ')) ||
|
|
86
|
+
claims['preferred_username'] ||
|
|
87
|
+
claims['email'] ||
|
|
88
|
+
claims['sub'] ||
|
|
89
|
+
'';
|
|
90
|
+
return {
|
|
91
|
+
sub: claims['sub'] ?? claims['oid'] ?? null,
|
|
92
|
+
name: display,
|
|
93
|
+
preferred_username: claims['preferred_username'] ?? claims['upn'] ?? null,
|
|
94
|
+
given_name: first,
|
|
95
|
+
family_name: last,
|
|
96
|
+
email: claims['email'] ?? null,
|
|
97
|
+
picture: claims['picture'] ?? null,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function decodeJwtPayload(jwt) {
|
|
101
|
+
if (!jwt)
|
|
102
|
+
return null;
|
|
103
|
+
const p = jwt.split('.')[1] ?? '';
|
|
104
|
+
try {
|
|
105
|
+
const pad = p.length % 4 ? '='.repeat(4 - (p.length % 4)) : '';
|
|
106
|
+
const b64 = p.replace(/-/g, '+').replace(/_/g, '/') + pad;
|
|
107
|
+
return JSON.parse(atob(b64));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function hasAny(have, required) {
|
|
114
|
+
if (!required?.length)
|
|
115
|
+
return true;
|
|
116
|
+
const set = new Set(have);
|
|
117
|
+
return required.some((r) => set.has(r.toLowerCase()));
|
|
118
|
+
}
|
|
119
|
+
function hasAll(have, required) {
|
|
120
|
+
if (!required?.length)
|
|
121
|
+
return true;
|
|
122
|
+
const set = new Set(have);
|
|
123
|
+
return required.every((r) => set.has(r.toLowerCase()));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Handles Microsoft Graph API profile loading via refresh token exchange.
|
|
127
|
+
// Only active when the IDP issuer is Azure AD.
|
|
128
|
+
class GraphProfileLoader {
|
|
129
|
+
oauth;
|
|
130
|
+
getIdpConfig;
|
|
131
|
+
accessToken = signal(null, ...(ngDevMode ? [{ debugName: "accessToken" }] : []));
|
|
132
|
+
accessTokenExp = 0;
|
|
133
|
+
constructor(oauth, getIdpConfig) {
|
|
134
|
+
this.oauth = oauth;
|
|
135
|
+
this.getIdpConfig = getIdpConfig;
|
|
136
|
+
}
|
|
137
|
+
isAzure() {
|
|
138
|
+
const issuer = this.oauth?.issuer ||
|
|
139
|
+
this.getIdpConfig()?.ISSUER ||
|
|
140
|
+
'';
|
|
141
|
+
return /login\.microsoftonline\.com/i.test(issuer) || /sts\.windows\.net/i.test(issuer);
|
|
142
|
+
}
|
|
143
|
+
async loadProfile() {
|
|
144
|
+
const token = await this.ensureToken();
|
|
145
|
+
if (!token)
|
|
146
|
+
return null;
|
|
147
|
+
const resp = await fetch('https://graph.microsoft.com/v1.0/me?$select=displayName,givenName,surname,mail,userPrincipalName,id', { headers: { Authorization: `Bearer ${token}` } });
|
|
148
|
+
if (!resp.ok)
|
|
149
|
+
return null;
|
|
150
|
+
const me = (await resp.json());
|
|
151
|
+
return {
|
|
152
|
+
sub: me['id'] ?? null,
|
|
153
|
+
name: me['displayName'] ?? null,
|
|
154
|
+
preferred_username: me['userPrincipalName'] ?? null,
|
|
155
|
+
given_name: me['givenName'] ?? null,
|
|
156
|
+
family_name: me['surname'] ?? null,
|
|
157
|
+
email: me['mail'] ?? me['userPrincipalName'] ?? null,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async ensureToken() {
|
|
161
|
+
const cached = this.accessToken();
|
|
162
|
+
if (cached && Date.now() < this.accessTokenExp)
|
|
163
|
+
return cached;
|
|
164
|
+
return this.exchangeRefreshToken();
|
|
165
|
+
}
|
|
166
|
+
async exchangeRefreshToken() {
|
|
167
|
+
const refresh = this.getRefreshToken();
|
|
168
|
+
const tokenEndpoint = this.oauth.tokenEndpoint;
|
|
169
|
+
const clientId = this.oauth?.clientId || this.getIdpConfig()?.CLIENT_ID;
|
|
170
|
+
if (!refresh || !tokenEndpoint || !clientId)
|
|
171
|
+
return null;
|
|
172
|
+
const scopes = this.getGraphScopes();
|
|
173
|
+
const body = new URLSearchParams({
|
|
174
|
+
grant_type: 'refresh_token',
|
|
175
|
+
client_id: clientId,
|
|
176
|
+
refresh_token: refresh,
|
|
177
|
+
scope: scopes.join(' '),
|
|
178
|
+
});
|
|
179
|
+
const resp = await fetch(tokenEndpoint, {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
182
|
+
body,
|
|
183
|
+
});
|
|
184
|
+
if (!resp.ok)
|
|
185
|
+
return null;
|
|
186
|
+
const json = (await resp.json());
|
|
187
|
+
const at = json['access_token'];
|
|
188
|
+
const expiresIn = Number(json['expires_in'] ?? 3600);
|
|
189
|
+
if (at) {
|
|
190
|
+
this.accessToken.set(at);
|
|
191
|
+
this.accessTokenExp = Date.now() + Math.max(0, (expiresIn - 60) * 1000);
|
|
192
|
+
try {
|
|
193
|
+
const rt = json['refresh_token'];
|
|
194
|
+
if (rt) {
|
|
195
|
+
const storage = this.oauth?.storage;
|
|
196
|
+
storage?.setItem('refresh_token', rt);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// ignore
|
|
201
|
+
}
|
|
202
|
+
return at;
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
getRefreshToken() {
|
|
207
|
+
try {
|
|
208
|
+
const o = this.oauth;
|
|
209
|
+
const rt = o.getRefreshToken?.() ?? o.storage?.getItem('refresh_token') ?? null;
|
|
210
|
+
return typeof rt === 'string' && rt ? rt : null;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
getGraphScopes() {
|
|
217
|
+
const idp = this.getIdpConfig();
|
|
218
|
+
const scopes = idp?.GRAPH_SCOPES || 'https://graph.microsoft.com/User.Read openid profile';
|
|
219
|
+
return scopes.trim().split(/\s+/).filter(Boolean);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Resolves and builds the user profile from OIDC claims, userinfo endpoint, and Graph API.
|
|
224
|
+
class ProfileResolver {
|
|
225
|
+
userinfo;
|
|
226
|
+
rolesSignal;
|
|
227
|
+
oauth;
|
|
228
|
+
graphLoader;
|
|
229
|
+
getPayload;
|
|
230
|
+
profile;
|
|
231
|
+
constructor(userinfo, rolesSignal, oauth, graphLoader, getPayload) {
|
|
232
|
+
this.userinfo = userinfo;
|
|
233
|
+
this.rolesSignal = rolesSignal;
|
|
234
|
+
this.oauth = oauth;
|
|
235
|
+
this.graphLoader = graphLoader;
|
|
236
|
+
this.getPayload = getPayload;
|
|
237
|
+
this.profile = computed(() => this.buildProfile(), ...(ngDevMode ? [{ debugName: "profile" }] : []));
|
|
238
|
+
}
|
|
239
|
+
async loadFromIdp() {
|
|
240
|
+
const roles = this.rolesSignal();
|
|
241
|
+
try {
|
|
242
|
+
const info = (await this.oauth.loadUserProfile());
|
|
243
|
+
const claims = this.resolveClaims();
|
|
244
|
+
let userinfo = { ...mapClaimsToUserinfo(claims), ...info };
|
|
245
|
+
if (this.graphLoader.isAzure()) {
|
|
246
|
+
const graphInfo = await this.graphLoader.loadProfile();
|
|
247
|
+
if (graphInfo)
|
|
248
|
+
userinfo = { ...userinfo, ...graphInfo };
|
|
249
|
+
}
|
|
250
|
+
userinfo['roles'] = Array.isArray(info?.['roles']) && info['roles'].length
|
|
251
|
+
? info['roles'] : roles;
|
|
252
|
+
this.userinfo.set(userinfo);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
await this.loadFallback(roles);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
clear() {
|
|
259
|
+
this.userinfo.set(null);
|
|
260
|
+
}
|
|
261
|
+
async loadFallback(roles) {
|
|
262
|
+
if (this.graphLoader.isAzure()) {
|
|
263
|
+
try {
|
|
264
|
+
const graphInfo = await this.graphLoader.loadProfile();
|
|
265
|
+
if (graphInfo) {
|
|
266
|
+
const claims = this.resolveClaims();
|
|
267
|
+
this.userinfo.set({ ...mapClaimsToUserinfo(claims), ...graphInfo, roles });
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
// Graph fallback failed
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const claims = this.resolveClaims();
|
|
276
|
+
this.userinfo.set({ ...mapClaimsToUserinfo(claims), roles });
|
|
277
|
+
}
|
|
278
|
+
resolveClaims() {
|
|
279
|
+
return (this.oauth.getIdentityClaims() ?? this.getPayload()) ?? {};
|
|
280
|
+
}
|
|
281
|
+
buildProfile() {
|
|
282
|
+
const c = this.userinfo();
|
|
283
|
+
if (!c)
|
|
284
|
+
return null;
|
|
285
|
+
const first = (c['given_name'] ?? c['givenName'] ?? c['firstName'] ?? null);
|
|
286
|
+
const last = (c['family_name'] ?? c['familyName'] ?? c['lastName'] ?? null);
|
|
287
|
+
const display = (c['name'] ?? [first, last].filter(Boolean).join(' ')) ||
|
|
288
|
+
c['preferred_username'] || c['email'] || c['sub'] || '';
|
|
289
|
+
return {
|
|
290
|
+
sub: c['sub'] ?? null,
|
|
291
|
+
displayName: display,
|
|
292
|
+
firstName: first,
|
|
293
|
+
lastName: last,
|
|
294
|
+
email: c['email'] ?? null,
|
|
295
|
+
username: c['preferred_username'] ?? c['upn'] ?? null,
|
|
296
|
+
pictureUrl: c['picture'] ?? null,
|
|
297
|
+
roles: Array.isArray(c['roles']) ? c['roles'] : this.rolesSignal(),
|
|
298
|
+
raw: c,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Manages token lifecycle: schedules automatic refresh before expiration
|
|
304
|
+
// and falls back to logout if refresh fails.
|
|
305
|
+
class TokenManager {
|
|
306
|
+
onExpired;
|
|
307
|
+
refreshTimer = null;
|
|
308
|
+
// Refresh 60 seconds before expiration to avoid using an expired token.
|
|
309
|
+
REFRESH_BEFORE_MS = 60_000;
|
|
310
|
+
constructor(onExpired) {
|
|
311
|
+
this.onExpired = onExpired;
|
|
312
|
+
}
|
|
313
|
+
scheduleAutoRefresh(oauth) {
|
|
314
|
+
this.clearSchedule();
|
|
315
|
+
const expMs = oauth.getAccessTokenExpiration?.();
|
|
316
|
+
if (!expMs || expMs <= 0)
|
|
317
|
+
return;
|
|
318
|
+
const delay = Math.max(0, expMs - Date.now() - this.REFRESH_BEFORE_MS);
|
|
319
|
+
this.refreshTimer = setTimeout(() => {
|
|
320
|
+
oauth.refreshToken()
|
|
321
|
+
.then(() => this.scheduleAutoRefresh(oauth))
|
|
322
|
+
.catch(() => this.onExpired());
|
|
323
|
+
}, delay);
|
|
324
|
+
}
|
|
325
|
+
clearSchedule() {
|
|
326
|
+
if (this.refreshTimer) {
|
|
327
|
+
clearTimeout(this.refreshTimer);
|
|
328
|
+
this.refreshTimer = null;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Plain class (no @Injectable) - provided via useFactory with deps.
|
|
334
|
+
class SecurityService {
|
|
335
|
+
isBrowser;
|
|
336
|
+
oauth;
|
|
337
|
+
router;
|
|
338
|
+
getIdpConfig;
|
|
339
|
+
oauthEvt;
|
|
340
|
+
accessToken;
|
|
341
|
+
payload;
|
|
342
|
+
tokenManager;
|
|
343
|
+
profileResolver;
|
|
344
|
+
isLoggedIn;
|
|
345
|
+
profile;
|
|
346
|
+
rolesSignal;
|
|
347
|
+
scopesSignal;
|
|
348
|
+
constructor(isBrowser, oauth, router, getIdpConfig, destroyRef) {
|
|
349
|
+
this.isBrowser = isBrowser;
|
|
350
|
+
this.oauth = oauth;
|
|
351
|
+
this.router = router;
|
|
352
|
+
this.getIdpConfig = getIdpConfig;
|
|
353
|
+
this.tokenManager = new TokenManager(() => this.logout());
|
|
354
|
+
const graphLoader = new GraphProfileLoader(oauth, getIdpConfig);
|
|
355
|
+
this.oauthEvt = signal(null, ...(ngDevMode ? [{ debugName: "oauthEvt" }] : []));
|
|
356
|
+
this.accessToken = computed(() => (this.oauthEvt(), this.oauth.getAccessToken() ?? null), ...(ngDevMode ? [{ debugName: "accessToken" }] : []));
|
|
357
|
+
this.payload = computed(() => decodeJwtPayload(this.accessToken()), ...(ngDevMode ? [{ debugName: "payload" }] : []));
|
|
358
|
+
this.isLoggedIn = computed(() => (this.oauthEvt(), this.oauth.hasValidAccessToken()), ...(ngDevMode ? [{ debugName: "isLoggedIn" }] : []));
|
|
359
|
+
this.scopesSignal = computed(() => extractScopes(this.payload() ?? {}), ...(ngDevMode ? [{ debugName: "scopesSignal" }] : []));
|
|
360
|
+
this.rolesSignal = computed(() => {
|
|
361
|
+
const claims = this.payload() ?? this.oauth.getIdentityClaims() ?? {};
|
|
362
|
+
return extractRoles(claims);
|
|
363
|
+
}, ...(ngDevMode ? [{ debugName: "rolesSignal" }] : []));
|
|
364
|
+
const userinfo = signal(null, ...(ngDevMode ? [{ debugName: "userinfo" }] : []));
|
|
365
|
+
this.profileResolver = new ProfileResolver(userinfo, this.rolesSignal, oauth, graphLoader, () => this.payload());
|
|
366
|
+
this.profile = this.profileResolver.profile;
|
|
367
|
+
if (this.isBrowser) {
|
|
368
|
+
this.subscribeToOAuthEvents();
|
|
369
|
+
destroyRef.onDestroy(() => this.tokenManager.clearSchedule());
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async init() {
|
|
373
|
+
if (!this.isBrowser)
|
|
374
|
+
return;
|
|
375
|
+
const idp = this.getIdpConfig();
|
|
376
|
+
if (!idp) {
|
|
377
|
+
console.warn('[SECURITY] No IDP config found, skipping init');
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
// Force sessionStorage so tokens and nonce survive redirects.
|
|
381
|
+
// The DI override in provideSecurity() may not take effect due to
|
|
382
|
+
// module duplication with symlinked libraries, so we set it explicitly.
|
|
383
|
+
this.oauth.setStorage(sessionStorage);
|
|
384
|
+
const authConfig = this.buildAuthConfig(idp);
|
|
385
|
+
this.oauth.configure(authConfig);
|
|
386
|
+
try {
|
|
387
|
+
await this.oauth.loadDiscoveryDocumentAndTryLogin();
|
|
388
|
+
}
|
|
389
|
+
catch (err) {
|
|
390
|
+
console.error('[SECURITY] loadDiscoveryDocumentAndTryLogin failed:', err);
|
|
391
|
+
}
|
|
392
|
+
if (this.oauth.hasValidAccessToken()) {
|
|
393
|
+
this.tokenManager.scheduleAutoRefresh(this.oauth);
|
|
394
|
+
await this.profileResolver.loadFromIdp();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
token() { return this.accessToken(); }
|
|
398
|
+
isAuthenticated() { return this.oauth.hasValidAccessToken(); }
|
|
399
|
+
roles() { return this.rolesSignal(); }
|
|
400
|
+
scopes() { return this.scopesSignal(); }
|
|
401
|
+
hasAnyRole(required) { return hasAny(this.rolesSignal(), required); }
|
|
402
|
+
hasAllRoles(required) { return hasAll(this.rolesSignal(), required); }
|
|
403
|
+
hasAnyScope(required) { return hasAny(this.scopesSignal(), required); }
|
|
404
|
+
login(target) {
|
|
405
|
+
const rel = target || this.router.url || '/';
|
|
406
|
+
this.oauth.initLoginFlow(stateEncode({ rel, t: Date.now() }));
|
|
407
|
+
}
|
|
408
|
+
logout() {
|
|
409
|
+
const idp = this.getIdpConfig();
|
|
410
|
+
this.oauth.logOut({ post_logout_redirect_uri: idp?.POST_LOGOUT_REDIRECT_URI });
|
|
411
|
+
}
|
|
412
|
+
// -- Private --
|
|
413
|
+
buildAuthConfig(idp) {
|
|
414
|
+
// Use the configured REDIRECT_URI path but resolve it against the current
|
|
415
|
+
// origin so that port changes in development (e.g. Vite picking a random
|
|
416
|
+
// port when 4200 is busy) don't break the nonce/PKCE validation.
|
|
417
|
+
const redirectUri = this.resolveRedirectUri(idp.REDIRECT_URI);
|
|
418
|
+
const postLogoutRedirectUri = this.resolveRedirectUri(idp.POST_LOGOUT_REDIRECT_URI);
|
|
419
|
+
return {
|
|
420
|
+
issuer: idp.ISSUER,
|
|
421
|
+
clientId: idp.CLIENT_ID,
|
|
422
|
+
redirectUri,
|
|
423
|
+
postLogoutRedirectUri,
|
|
424
|
+
responseType: idp.RESPONSE_TYPE || 'code',
|
|
425
|
+
scope: idp.SCOPE || 'openid profile email offline_access',
|
|
426
|
+
strictDiscoveryDocumentValidation: idp.STRICT_DISCOVERY_DOCUMENT_VALIDATION ?? true,
|
|
427
|
+
showDebugInformation: idp.SHOW_DEBUG_INFORMATION ?? false,
|
|
428
|
+
useSilentRefresh: false,
|
|
429
|
+
sessionChecksEnabled: false,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
resolveRedirectUri(uri) {
|
|
433
|
+
if (!uri)
|
|
434
|
+
return undefined;
|
|
435
|
+
try {
|
|
436
|
+
const parsed = new URL(uri);
|
|
437
|
+
// Replace origin (scheme + host + port) with the actual runtime origin
|
|
438
|
+
return window.location.origin + parsed.pathname;
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
return uri;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
subscribeToOAuthEvents() {
|
|
445
|
+
const evtSignal = this.oauthEvt;
|
|
446
|
+
this.oauth.events.pipe(startWith(null)).subscribe((e) => {
|
|
447
|
+
evtSignal.set(e);
|
|
448
|
+
if (!e)
|
|
449
|
+
return;
|
|
450
|
+
if (e instanceof OAuthErrorEvent) {
|
|
451
|
+
console.error('[SECURITY] OAuthErrorEvent:', e.type, e.params, e.reason);
|
|
452
|
+
this.tokenManager.clearSchedule();
|
|
453
|
+
this.profileResolver.clear();
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
switch (e.type) {
|
|
457
|
+
case 'token_received':
|
|
458
|
+
this.tokenManager.scheduleAutoRefresh(this.oauth);
|
|
459
|
+
void this.profileResolver.loadFromIdp();
|
|
460
|
+
break;
|
|
461
|
+
case 'token_refreshed':
|
|
462
|
+
this.tokenManager.scheduleAutoRefresh(this.oauth);
|
|
463
|
+
void this.profileResolver.loadFromIdp();
|
|
464
|
+
break;
|
|
465
|
+
case 'logout':
|
|
466
|
+
this.tokenManager.clearSchedule();
|
|
467
|
+
this.profileResolver.clear();
|
|
468
|
+
break;
|
|
469
|
+
case 'session_terminated':
|
|
470
|
+
case 'token_refresh_error':
|
|
471
|
+
this.logout();
|
|
472
|
+
break;
|
|
473
|
+
default:
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Provides SecurityService + OAuthService + APP_INITIALIZER for auth bootstrap.
|
|
481
|
+
// Uses provideOAuthClient() from angular-oauth2-oidc for OAuthService registration.
|
|
482
|
+
// IMPORTANT: The consumer app must set `preserveSymlinks: true` in angular.json
|
|
483
|
+
// to prevent Vite from bundling a duplicate copy of @angular/core from the
|
|
484
|
+
// library's node_modules (which would cause NG0203 at runtime).
|
|
485
|
+
function provideSecurity() {
|
|
486
|
+
return makeEnvironmentProviders([
|
|
487
|
+
provideOAuthClient(),
|
|
488
|
+
{
|
|
489
|
+
provide: SecurityService,
|
|
490
|
+
useFactory: (platformId, oauth, router, configService, destroyRef) => new SecurityService(isPlatformBrowser(platformId), oauth, router, () => configService.get('IDP') ?? null, destroyRef),
|
|
491
|
+
deps: [PLATFORM_ID, OAuthService, Router, ConfigService, DestroyRef],
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
provide: APP_INITIALIZER,
|
|
495
|
+
multi: true,
|
|
496
|
+
useFactory: (securityService) => () => securityService.init(),
|
|
497
|
+
deps: [SecurityService],
|
|
498
|
+
},
|
|
499
|
+
]);
|
|
500
|
+
}
|
|
501
|
+
// Testing provider: provides a no-op SecurityService.
|
|
502
|
+
function provideSecurityTesting() {
|
|
503
|
+
return [
|
|
504
|
+
{
|
|
505
|
+
provide: SecurityService,
|
|
506
|
+
useFactory: (platformId, router) => new SecurityService(isPlatformBrowser(platformId), { events: { pipe: () => ({ subscribe: () => { } }) } }, router, () => null, { onDestroy: () => { } }),
|
|
507
|
+
deps: [PLATFORM_ID, Router],
|
|
508
|
+
},
|
|
509
|
+
];
|
|
11
510
|
}
|
|
12
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: EacArchInfrastructureSecurity, decorators: [{
|
|
13
|
-
type: Component,
|
|
14
|
-
args: [{ selector: 'eac-arch-eac-arch-infrastructure-security', imports: [], template: `
|
|
15
|
-
<p>
|
|
16
|
-
eac-arch-infrastructure-security works!
|
|
17
|
-
</p>
|
|
18
|
-
` }]
|
|
19
|
-
}] });
|
|
20
511
|
|
|
21
|
-
|
|
22
|
-
|
|
512
|
+
const authGuard = (_route, state) => {
|
|
513
|
+
const platformId = inject(PLATFORM_ID);
|
|
514
|
+
const security = inject(SecurityService);
|
|
515
|
+
if (!isPlatformBrowser(platformId))
|
|
516
|
+
return false;
|
|
517
|
+
if (!security.isAuthenticated()) {
|
|
518
|
+
security.login(state.url);
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
return true;
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
const authzGuard = (route) => {
|
|
525
|
+
const router = inject(Router);
|
|
526
|
+
const authService = inject(SecurityService);
|
|
527
|
+
if (!authService.isAuthenticated()) {
|
|
528
|
+
return router.parseUrl('/');
|
|
529
|
+
}
|
|
530
|
+
const data = route.data?.['auth'] ?? {};
|
|
531
|
+
const any = data.any ?? [];
|
|
532
|
+
const all = data.all ?? [];
|
|
533
|
+
const scopesAny = data.scopesAny ?? [];
|
|
534
|
+
const ok = authService.hasAnyRole(any) &&
|
|
535
|
+
authService.hasAllRoles(all) &&
|
|
536
|
+
authService.hasAnyScope(scopesAny);
|
|
537
|
+
return ok || router.parseUrl('/security/forbidden');
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
const authInterceptor = (req, next) => {
|
|
541
|
+
const config = inject(ConfigService);
|
|
542
|
+
const security = inject(SecurityService);
|
|
543
|
+
const requestUrl = req.url ?? '';
|
|
544
|
+
if (!requestUrl) {
|
|
545
|
+
return next(req);
|
|
546
|
+
}
|
|
547
|
+
const token = security.token();
|
|
548
|
+
if (!token)
|
|
549
|
+
return next(req);
|
|
550
|
+
const keys = config.get('SECURITY')?.SECURE_API_KEYS ?? [];
|
|
551
|
+
const isSecure = keys
|
|
552
|
+
.map(key => config.get(key))
|
|
553
|
+
.filter((url) => !!url)
|
|
554
|
+
.some(url => requestUrl.startsWith(url));
|
|
555
|
+
if (isSecure) {
|
|
556
|
+
return next(req.clone({
|
|
557
|
+
setHeaders: { Authorization: `Bearer ${token}` },
|
|
558
|
+
}));
|
|
559
|
+
}
|
|
560
|
+
return next(req);
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Landing component for the OAuth callback route.
|
|
565
|
+
*
|
|
566
|
+
* The APP_INITIALIZER (SecurityService.init) should have already
|
|
567
|
+
* exchanged the authorization code. If it failed for any reason
|
|
568
|
+
* this component retries the code exchange as a fallback.
|
|
23
569
|
*/
|
|
570
|
+
class OidcCallback {
|
|
571
|
+
oauth = inject(OAuthService);
|
|
572
|
+
router = inject(Router);
|
|
573
|
+
pid = inject(PLATFORM_ID);
|
|
574
|
+
async ngOnInit() {
|
|
575
|
+
if (!isPlatformBrowser(this.pid))
|
|
576
|
+
return;
|
|
577
|
+
// If init() already exchanged the code, just navigate.
|
|
578
|
+
// Otherwise retry the exchange here (fallback).
|
|
579
|
+
if (!this.oauth.hasValidAccessToken()) {
|
|
580
|
+
try {
|
|
581
|
+
await this.oauth.loadDiscoveryDocument();
|
|
582
|
+
await this.oauth.tryLoginCodeFlow();
|
|
583
|
+
}
|
|
584
|
+
catch (err) {
|
|
585
|
+
console.error('[SECURITY] OidcCallback code exchange failed:', err);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (this.oauth.hasValidAccessToken()) {
|
|
589
|
+
const obj = (stateDecodeFromUrlParam(this.oauth.state) ?? {});
|
|
590
|
+
const rel = safeRel(obj['rel'] || '/');
|
|
591
|
+
window.history.replaceState({}, document.title, '/');
|
|
592
|
+
this.router.navigateByUrl(rel, { replaceUrl: true });
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
console.warn('[SECURITY] No valid token after callback, redirecting home');
|
|
596
|
+
this.router.navigateByUrl('/', { replaceUrl: true });
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: OidcCallback, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
600
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: OidcCallback, isStandalone: true, selector: "eac-oidc-callback", ngImport: i0, template: '', isInline: true, styles: [""] });
|
|
601
|
+
}
|
|
602
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: OidcCallback, decorators: [{
|
|
603
|
+
type: Component,
|
|
604
|
+
args: [{ selector: 'eac-oidc-callback', standalone: true, template: '' }]
|
|
605
|
+
}] });
|
|
24
606
|
|
|
25
607
|
/**
|
|
26
608
|
* Generated bundle index. Do not edit.
|
|
27
609
|
*/
|
|
28
610
|
|
|
29
|
-
export {
|
|
611
|
+
export { OidcCallback, SecurityService, authGuard, authInterceptor, authzGuard, provideSecurity, safeRel, stateDecode, stateDecodeFromUrlParam, stateEncode };
|
|
30
612
|
//# sourceMappingURL=eac-arch-infrastructure-security.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eac-arch-infrastructure-security.mjs","sources":["../../../projects/eac-arch-infrastructure-security/src/lib/eac-arch-infrastructure-security.ts","../../../projects/eac-arch-infrastructure-security/src/public-api.ts","../../../projects/eac-arch-infrastructure-security/src/eac-arch-infrastructure-security.ts"],"sourcesContent":["import { Component } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'eac-arch-eac-arch-infrastructure-security',\r\n imports: [],\r\n template: `\r\n <p>\r\n eac-arch-infrastructure-security works!\r\n </p>\r\n `,\r\n styles: ``,\r\n})\r\nexport class EacArchInfrastructureSecurity {\r\n\r\n}\r\n","/*\r\n * Public API Surface of eac-arch-infrastructure-security\r\n */\r\n\r\nexport * from './lib/eac-arch-infrastructure-security';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;MAYa,6BAA6B,CAAA;uGAA7B,6BAA6B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA7B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,6BAA6B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAP9B,CAAA;;;;AAIT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAGU,6BAA6B,EAAA,UAAA,EAAA,CAAA;kBAVzC,SAAS;+BACE,2CAA2C,EAAA,OAAA,EAC5C,EAAE,EAAA,QAAA,EACD,CAAA;;;;AAIT,EAAA,CAAA,EAAA;;;ACTH;;AAEG;;ACFH;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"eac-arch-infrastructure-security.mjs","sources":["../../../projects/eac-arch-infrastructure-security/src/lib/utils/oauth-state.util.ts","../../../projects/eac-arch-infrastructure-security/src/lib/services/claims-parser.ts","../../../projects/eac-arch-infrastructure-security/src/lib/services/graph-profile-loader.ts","../../../projects/eac-arch-infrastructure-security/src/lib/services/profile-resolver.ts","../../../projects/eac-arch-infrastructure-security/src/lib/services/token-manager.ts","../../../projects/eac-arch-infrastructure-security/src/lib/services/security.service.ts","../../../projects/eac-arch-infrastructure-security/src/lib/security.provider.ts","../../../projects/eac-arch-infrastructure-security/src/lib/guards/auth.guard.ts","../../../projects/eac-arch-infrastructure-security/src/lib/guards/authz.guard.ts","../../../projects/eac-arch-infrastructure-security/src/lib/interceptors/auth.interceptor.ts","../../../projects/eac-arch-infrastructure-security/src/lib/components/oidc-callback.ts","../../../projects/eac-arch-infrastructure-security/src/eac-arch-infrastructure-security.ts"],"sourcesContent":["const DEFAULT_SEPARATOR = ';';\n\nexport function stateEncode(obj: unknown): string {\n const s = JSON.stringify(obj);\n return btoa(s).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '');\n}\n\nexport function stateDecode(s: string): unknown | null {\n try {\n const pad = s.length % 4 ? '='.repeat(4 - (s.length % 4)) : '';\n const b64 = s.replace(/-/g, '+').replace(/_/g, '/') + pad;\n return JSON.parse(atob(b64));\n } catch {\n return null;\n }\n}\n\nexport function stateDecodeFromUrlParam(s?: string | null): Record<string, unknown> | null {\n if (!s) return null;\n let decoded = s;\n if (/%[0-9A-Fa-f]{2}/.test(decoded)) {\n try {\n decoded = decodeURIComponent(decoded);\n } catch {\n /* ignore */\n }\n }\n return stateDecode(decoded) as Record<string, unknown> | null;\n}\n\nexport function safeRel(rel: unknown): string {\n let r = typeof rel === 'string' ? rel : '/';\n if (!r.startsWith('/')) r = '/' + r;\n try {\n const u = new URL(r, window.location.origin);\n if (u.origin !== window.location.origin) return '/';\n } catch {\n return '/';\n }\n return r;\n}\n","// Pure functions for extracting roles, scopes and user info from JWT/OIDC claims.\n// Supports both Azure AD and Keycloak claim formats.\n\nexport function extractRoles(claims: Record<string, unknown>): string[] {\n const out = new Set<string>();\n\n // Azure AD\n if (Array.isArray(claims['roles']))\n for (const r of claims['roles'] as string[]) out.add(String(r).toLowerCase());\n if (Array.isArray(claims['groups']))\n for (const g of claims['groups'] as string[]) out.add(String(g).toLowerCase());\n\n // Keycloak\n const realmAccess = claims['realm_access'] as { roles?: string[] } | undefined;\n if (Array.isArray(realmAccess?.roles))\n for (const r of realmAccess!.roles!) out.add(String(r).toLowerCase());\n\n const ra = claims['resource_access'] as Record<string, { roles?: string[] } | undefined> | undefined;\n if (ra)\n for (const k of Object.keys(ra)) ra[k]?.roles?.forEach((r: string) => out.add(String(r).toLowerCase()));\n\n return Array.from(out);\n}\n\nexport function extractScopes(claims: Record<string, unknown>): string[] {\n // Azure AD uses 'scp', Keycloak uses 'scope'\n const raw = (claims['scp'] ?? claims['scope']) as string | undefined;\n if (typeof raw !== 'string' || !raw) return [];\n return raw.trim().split(/\\s+/).map((s: string) => s.toLowerCase());\n}\n\nexport function mapClaimsToUserinfo(claims: Record<string, unknown>): Record<string, unknown> {\n const first = (claims['given_name'] ?? claims['givenName'] ?? claims['firstName'] ?? null) as string | null;\n const last = (claims['family_name'] ?? claims['familyName'] ?? claims['lastName'] ?? null) as string | null;\n const display =\n ((claims['name'] as string) ?? [first, last].filter(Boolean).join(' ')) ||\n (claims['preferred_username'] as string) ||\n (claims['email'] as string) ||\n (claims['sub'] as string) ||\n '';\n return {\n sub: claims['sub'] ?? claims['oid'] ?? null,\n name: display,\n preferred_username: claims['preferred_username'] ?? claims['upn'] ?? null,\n given_name: first,\n family_name: last,\n email: claims['email'] ?? null,\n picture: claims['picture'] ?? null,\n };\n}\n\nexport function decodeJwtPayload(jwt: string | null): Record<string, unknown> | null {\n if (!jwt) return null;\n const p = jwt.split('.')[1] ?? '';\n try {\n const pad = p.length % 4 ? '='.repeat(4 - (p.length % 4)) : '';\n const b64 = p.replace(/-/g, '+').replace(/_/g, '/') + pad;\n return JSON.parse(atob(b64));\n } catch {\n return null;\n }\n}\n\nexport function hasAny(have: string[], required: string[]): boolean {\n if (!required?.length) return true;\n const set = new Set(have);\n return required.some((r) => set.has(r.toLowerCase()));\n}\n\nexport function hasAll(have: string[], required: string[]): boolean {\n if (!required?.length) return true;\n const set = new Set(have);\n return required.every((r) => set.has(r.toLowerCase()));\n}\n","import { signal } from '@angular/core';\nimport { OAuthService } from 'angular-oauth2-oidc';\nimport { IdpConfig } from '../models/idp-config';\n\n// Handles Microsoft Graph API profile loading via refresh token exchange.\n// Only active when the IDP issuer is Azure AD.\nexport class GraphProfileLoader {\n private readonly accessToken = signal<string | null>(null);\n private accessTokenExp = 0;\n\n constructor(\n private readonly oauth: OAuthService,\n private readonly getIdpConfig: () => IdpConfig | null,\n ) {}\n\n isAzure(): boolean {\n const issuer =\n (this.oauth as unknown as { issuer?: string })?.issuer ||\n this.getIdpConfig()?.ISSUER ||\n '';\n return /login\\.microsoftonline\\.com/i.test(issuer) || /sts\\.windows\\.net/i.test(issuer);\n }\n\n async loadProfile(): Promise<Record<string, unknown> | null> {\n const token = await this.ensureToken();\n if (!token) return null;\n\n const resp = await fetch(\n 'https://graph.microsoft.com/v1.0/me?$select=displayName,givenName,surname,mail,userPrincipalName,id',\n { headers: { Authorization: `Bearer ${token}` } },\n );\n\n if (!resp.ok) return null;\n\n const me = (await resp.json()) as Record<string, unknown>;\n return {\n sub: me['id'] ?? null,\n name: me['displayName'] ?? null,\n preferred_username: me['userPrincipalName'] ?? null,\n given_name: me['givenName'] ?? null,\n family_name: me['surname'] ?? null,\n email: me['mail'] ?? me['userPrincipalName'] ?? null,\n };\n }\n\n private async ensureToken(): Promise<string | null> {\n const cached = this.accessToken();\n if (cached && Date.now() < this.accessTokenExp) return cached;\n return this.exchangeRefreshToken();\n }\n\n private async exchangeRefreshToken(): Promise<string | null> {\n const refresh = this.getRefreshToken();\n const tokenEndpoint = this.oauth.tokenEndpoint;\n const clientId =\n (this.oauth as unknown as { clientId?: string })?.clientId || this.getIdpConfig()?.CLIENT_ID;\n\n if (!refresh || !tokenEndpoint || !clientId) return null;\n\n const scopes = this.getGraphScopes();\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: clientId,\n refresh_token: refresh,\n scope: scopes.join(' '),\n });\n\n const resp = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body,\n });\n\n if (!resp.ok) return null;\n\n const json = (await resp.json()) as Record<string, unknown>;\n const at = json['access_token'] as string | undefined;\n const expiresIn = Number(json['expires_in'] ?? 3600);\n\n if (at) {\n this.accessToken.set(at);\n this.accessTokenExp = Date.now() + Math.max(0, (expiresIn - 60) * 1000);\n\n try {\n const rt = json['refresh_token'] as string | undefined;\n if (rt) {\n const storage = (this.oauth as unknown as { storage?: { setItem(k: string, v: string): void } })?.storage;\n storage?.setItem('refresh_token', rt);\n }\n } catch {\n // ignore\n }\n\n return at;\n }\n\n return null;\n }\n\n private getRefreshToken(): string | null {\n try {\n const o = this.oauth as unknown as {\n getRefreshToken?: () => string;\n storage?: { getItem(key: string): string | null };\n };\n const rt = o.getRefreshToken?.() ?? o.storage?.getItem('refresh_token') ?? null;\n return typeof rt === 'string' && rt ? rt : null;\n } catch {\n return null;\n }\n }\n\n private getGraphScopes(): string[] {\n const idp = this.getIdpConfig();\n const scopes = idp?.GRAPH_SCOPES || 'https://graph.microsoft.com/User.Read openid profile';\n return scopes.trim().split(/\\s+/).filter(Boolean);\n }\n}\n","import { computed, Signal, WritableSignal } from '@angular/core';\nimport { OAuthService } from 'angular-oauth2-oidc';\n\nimport { UserProfile } from '../models/user-profile';\nimport { mapClaimsToUserinfo } from './claims-parser';\nimport { GraphProfileLoader } from './graph-profile-loader';\n\n// Resolves and builds the user profile from OIDC claims, userinfo endpoint, and Graph API.\nexport class ProfileResolver {\n readonly profile: Signal<UserProfile | null>;\n\n constructor(\n private readonly userinfo: WritableSignal<Record<string, unknown> | null>,\n private readonly rolesSignal: Signal<string[]>,\n private readonly oauth: OAuthService,\n private readonly graphLoader: GraphProfileLoader,\n private readonly getPayload: () => Record<string, unknown> | null,\n ) {\n this.profile = computed(() => this.buildProfile());\n }\n\n async loadFromIdp(): Promise<void> {\n const roles = this.rolesSignal();\n try {\n const info = (await this.oauth.loadUserProfile()) as Record<string, unknown>;\n const claims = this.resolveClaims();\n let userinfo = { ...mapClaimsToUserinfo(claims), ...info } as Record<string, unknown>;\n\n if (this.graphLoader.isAzure()) {\n const graphInfo = await this.graphLoader.loadProfile();\n if (graphInfo) userinfo = { ...userinfo, ...graphInfo };\n }\n\n userinfo['roles'] = Array.isArray(info?.['roles']) && (info['roles'] as unknown[]).length\n ? info['roles'] : roles;\n\n this.userinfo.set(userinfo);\n } catch {\n await this.loadFallback(roles);\n }\n }\n\n clear(): void {\n this.userinfo.set(null);\n }\n\n private async loadFallback(roles: string[]): Promise<void> {\n if (this.graphLoader.isAzure()) {\n try {\n const graphInfo = await this.graphLoader.loadProfile();\n if (graphInfo) {\n const claims = this.resolveClaims();\n this.userinfo.set({ ...mapClaimsToUserinfo(claims), ...graphInfo, roles });\n return;\n }\n } catch {\n // Graph fallback failed\n }\n }\n\n const claims = this.resolveClaims();\n this.userinfo.set({ ...mapClaimsToUserinfo(claims), roles });\n }\n\n private resolveClaims(): Record<string, unknown> {\n return ((this.oauth.getIdentityClaims() as Record<string, unknown>) ?? this.getPayload()) ?? {};\n }\n\n private buildProfile(): UserProfile | null {\n const c = this.userinfo();\n if (!c) return null;\n\n const first = (c['given_name'] ?? c['givenName'] ?? c['firstName'] ?? null) as string | null;\n const last = (c['family_name'] ?? c['familyName'] ?? c['lastName'] ?? null) as string | null;\n const display = ((c['name'] as string) ?? [first, last].filter(Boolean).join(' ')) ||\n (c['preferred_username'] as string) || (c['email'] as string) || (c['sub'] as string) || '';\n\n return {\n sub: (c['sub'] as string) ?? null,\n displayName: display,\n firstName: first,\n lastName: last,\n email: (c['email'] as string) ?? null,\n username: (c['preferred_username'] as string) ?? (c['upn'] as string) ?? null,\n pictureUrl: (c['picture'] as string) ?? null,\n roles: Array.isArray(c['roles']) ? (c['roles'] as string[]) : this.rolesSignal(),\n raw: c,\n };\n }\n}\n","import { OAuthService } from 'angular-oauth2-oidc';\n\n// Manages token lifecycle: schedules automatic refresh before expiration\n// and falls back to logout if refresh fails.\nexport class TokenManager {\n private refreshTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Refresh 60 seconds before expiration to avoid using an expired token.\n private readonly REFRESH_BEFORE_MS = 60_000;\n\n constructor(private readonly onExpired: () => void) {}\n\n scheduleAutoRefresh(oauth: OAuthService): void {\n this.clearSchedule();\n\n const expMs = oauth.getAccessTokenExpiration?.();\n if (!expMs || expMs <= 0) return;\n\n const delay = Math.max(0, expMs - Date.now() - this.REFRESH_BEFORE_MS);\n\n this.refreshTimer = setTimeout(() => {\n oauth.refreshToken()\n .then(() => this.scheduleAutoRefresh(oauth))\n .catch(() => this.onExpired());\n }, delay);\n }\n\n clearSchedule(): void {\n if (this.refreshTimer) {\n clearTimeout(this.refreshTimer);\n this.refreshTimer = null;\n }\n }\n}\n","import { computed, signal, Signal } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { startWith } from 'rxjs';\n\nimport { AuthConfig, OAuthErrorEvent, OAuthEvent, OAuthService } from 'angular-oauth2-oidc';\n\nimport { IdpConfig } from '../models/idp-config';\nimport { UserProfile } from '../models/user-profile';\n\nimport { stateEncode } from '../utils/oauth-state.util';\nimport { decodeJwtPayload, extractRoles, extractScopes, hasAll, hasAny } from './claims-parser';\nimport { GraphProfileLoader } from './graph-profile-loader';\nimport { ProfileResolver } from './profile-resolver';\nimport { TokenManager } from './token-manager';\n\n// Plain class (no @Injectable) - provided via useFactory with deps.\nexport class SecurityService {\n private readonly oauthEvt: Signal<OAuthEvent | null>;\n private readonly accessToken: Signal<string | null>;\n private readonly payload: Signal<Record<string, unknown> | null>;\n private readonly tokenManager: TokenManager;\n private readonly profileResolver: ProfileResolver;\n\n readonly isLoggedIn: Signal<boolean>;\n readonly profile: Signal<UserProfile | null>;\n readonly rolesSignal: Signal<string[]>;\n readonly scopesSignal: Signal<string[]>;\n\n constructor(\n private readonly isBrowser: boolean,\n private readonly oauth: OAuthService,\n private readonly router: Router,\n private readonly getIdpConfig: () => IdpConfig | null,\n destroyRef: { onDestroy: (fn: () => void) => void },\n ) {\n this.tokenManager = new TokenManager(() => this.logout());\n const graphLoader = new GraphProfileLoader(oauth, getIdpConfig);\n\n this.oauthEvt = signal<OAuthEvent | null>(null);\n this.accessToken = computed(() => (this.oauthEvt(), this.oauth.getAccessToken() ?? null));\n this.payload = computed(() => decodeJwtPayload(this.accessToken()));\n this.isLoggedIn = computed(() => (this.oauthEvt(), this.oauth.hasValidAccessToken()));\n this.scopesSignal = computed(() => extractScopes(this.payload() ?? {}));\n this.rolesSignal = computed(() => {\n const claims = this.payload() ?? (this.oauth.getIdentityClaims() as Record<string, unknown>) ?? {};\n return extractRoles(claims);\n });\n\n const userinfo = signal<Record<string, unknown> | null>(null);\n this.profileResolver = new ProfileResolver(userinfo, this.rolesSignal, oauth, graphLoader, () => this.payload());\n this.profile = this.profileResolver.profile;\n\n if (this.isBrowser) {\n this.subscribeToOAuthEvents();\n destroyRef.onDestroy(() => this.tokenManager.clearSchedule());\n }\n }\n\n async init(): Promise<void> {\n if (!this.isBrowser) return;\n\n const idp = this.getIdpConfig();\n if (!idp) {\n console.warn('[SECURITY] No IDP config found, skipping init');\n return;\n }\n\n // Force sessionStorage so tokens and nonce survive redirects.\n // The DI override in provideSecurity() may not take effect due to\n // module duplication with symlinked libraries, so we set it explicitly.\n this.oauth.setStorage(sessionStorage);\n\n const authConfig = this.buildAuthConfig(idp);\n this.oauth.configure(authConfig);\n\n try {\n await this.oauth.loadDiscoveryDocumentAndTryLogin();\n } catch (err) {\n console.error('[SECURITY] loadDiscoveryDocumentAndTryLogin failed:', err);\n }\n\n if (this.oauth.hasValidAccessToken()) {\n this.tokenManager.scheduleAutoRefresh(this.oauth);\n await this.profileResolver.loadFromIdp();\n }\n }\n\n token(): string | null { return this.accessToken(); }\n isAuthenticated(): boolean { return this.oauth.hasValidAccessToken(); }\n roles(): string[] { return this.rolesSignal(); }\n scopes(): string[] { return this.scopesSignal(); }\n hasAnyRole(required: string[]): boolean { return hasAny(this.rolesSignal(), required); }\n hasAllRoles(required: string[]): boolean { return hasAll(this.rolesSignal(), required); }\n hasAnyScope(required: string[]): boolean { return hasAny(this.scopesSignal(), required); }\n\n login(target?: string): void {\n const rel = target || this.router.url || '/';\n this.oauth.initLoginFlow(stateEncode({ rel, t: Date.now() }));\n }\n\n logout(): void {\n const idp = this.getIdpConfig();\n this.oauth.logOut({ post_logout_redirect_uri: idp?.POST_LOGOUT_REDIRECT_URI });\n }\n\n // -- Private --\n\n private buildAuthConfig(idp: IdpConfig): AuthConfig {\n // Use the configured REDIRECT_URI path but resolve it against the current\n // origin so that port changes in development (e.g. Vite picking a random\n // port when 4200 is busy) don't break the nonce/PKCE validation.\n const redirectUri = this.resolveRedirectUri(idp.REDIRECT_URI);\n const postLogoutRedirectUri = this.resolveRedirectUri(idp.POST_LOGOUT_REDIRECT_URI);\n\n return {\n issuer: idp.ISSUER,\n clientId: idp.CLIENT_ID,\n redirectUri,\n postLogoutRedirectUri,\n responseType: idp.RESPONSE_TYPE || 'code',\n scope: idp.SCOPE || 'openid profile email offline_access',\n strictDiscoveryDocumentValidation: idp.STRICT_DISCOVERY_DOCUMENT_VALIDATION ?? true,\n showDebugInformation: idp.SHOW_DEBUG_INFORMATION ?? false,\n useSilentRefresh: false,\n sessionChecksEnabled: false,\n };\n }\n\n private resolveRedirectUri(uri: string | undefined): string | undefined {\n if (!uri) return undefined;\n try {\n const parsed = new URL(uri);\n // Replace origin (scheme + host + port) with the actual runtime origin\n return window.location.origin + parsed.pathname;\n } catch {\n return uri;\n }\n }\n\n private subscribeToOAuthEvents(): void {\n const evtSignal = this.oauthEvt as ReturnType<typeof signal<OAuthEvent | null>>;\n\n this.oauth.events.pipe(startWith(null)).subscribe((e) => {\n evtSignal.set(e);\n if (!e) return;\n\n if (e instanceof OAuthErrorEvent) {\n console.error('[SECURITY] OAuthErrorEvent:', e.type, (e as OAuthErrorEvent).params, (e as OAuthErrorEvent).reason);\n this.tokenManager.clearSchedule();\n this.profileResolver.clear();\n return;\n }\n\n switch (e.type) {\n case 'token_received':\n this.tokenManager.scheduleAutoRefresh(this.oauth);\n void this.profileResolver.loadFromIdp();\n break;\n case 'token_refreshed':\n this.tokenManager.scheduleAutoRefresh(this.oauth);\n void this.profileResolver.loadFromIdp();\n break;\n case 'logout':\n this.tokenManager.clearSchedule();\n this.profileResolver.clear();\n break;\n case 'session_terminated':\n case 'token_refresh_error':\n this.logout();\n break;\n default:\n break;\n }\n });\n }\n\n}\n","import { APP_INITIALIZER, DestroyRef, EnvironmentProviders, makeEnvironmentProviders, PLATFORM_ID, Provider } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { Router } from '@angular/router';\nimport { OAuthService, provideOAuthClient } from 'angular-oauth2-oidc';\nimport { ConfigService } from '@eac-arch/infrastructure-config';\n\nimport { SecurityService } from './services/security.service';\nimport { IdpConfig } from './models/idp-config';\n\n// Provides SecurityService + OAuthService + APP_INITIALIZER for auth bootstrap.\n// Uses provideOAuthClient() from angular-oauth2-oidc for OAuthService registration.\n// IMPORTANT: The consumer app must set `preserveSymlinks: true` in angular.json\n// to prevent Vite from bundling a duplicate copy of @angular/core from the\n// library's node_modules (which would cause NG0203 at runtime).\nexport function provideSecurity(): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideOAuthClient(),\n\n {\n provide: SecurityService,\n useFactory: (\n platformId: object,\n oauth: OAuthService,\n router: Router,\n configService: ConfigService,\n destroyRef: DestroyRef,\n ) =>\n new SecurityService(\n isPlatformBrowser(platformId),\n oauth,\n router,\n () => configService.get<IdpConfig>('IDP') ?? null,\n destroyRef,\n ),\n deps: [PLATFORM_ID, OAuthService, Router, ConfigService, DestroyRef],\n },\n\n {\n provide: APP_INITIALIZER,\n multi: true,\n useFactory: (securityService: SecurityService) => () => securityService.init(),\n deps: [SecurityService],\n },\n ]);\n}\n\n// Testing provider: provides a no-op SecurityService.\nexport function provideSecurityTesting(): Provider[] {\n return [\n {\n provide: SecurityService,\n useFactory: (platformId: object, router: Router) =>\n new SecurityService(\n isPlatformBrowser(platformId),\n { events: { pipe: () => ({ subscribe: () => {} }) } } as unknown as OAuthService,\n router,\n () => null,\n { onDestroy: () => {} },\n ),\n deps: [PLATFORM_ID, Router],\n },\n ];\n}\n","import { inject, PLATFORM_ID } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { CanActivateFn } from '@angular/router';\nimport { SecurityService } from '../services/security.service';\n\nexport const authGuard: CanActivateFn = (_route, state) => {\n const platformId = inject(PLATFORM_ID);\n const security = inject(SecurityService);\n\n if (!isPlatformBrowser(platformId)) return false;\n\n if (!security.isAuthenticated()) {\n security.login(state.url);\n return false;\n }\n\n return true;\n};\n","import { inject } from '@angular/core';\nimport { CanActivateFn, Router } from '@angular/router';\nimport { SecurityService } from '../services/security.service';\n\nexport const authzGuard: CanActivateFn = (route) => {\n const router = inject(Router);\n const authService = inject(SecurityService);\n\n if (!authService.isAuthenticated()) {\n return router.parseUrl('/');\n }\n\n const data = (route.data as Record<string, unknown>)?.['auth'] as {\n any?: string[];\n all?: string[];\n scopesAny?: string[];\n } | undefined ?? {};\n\n const any: string[] = data.any ?? [];\n const all: string[] = data.all ?? [];\n const scopesAny: string[] = data.scopesAny ?? [];\n\n const ok =\n authService.hasAnyRole(any) &&\n authService.hasAllRoles(all) &&\n authService.hasAnyScope(scopesAny);\n\n return ok || router.parseUrl('/security/forbidden');\n};\n","import { HttpInterceptorFn } from '@angular/common/http';\nimport { inject } from '@angular/core';\nimport { SecurityService } from '../services/security.service';\nimport { ConfigService } from '@eac-arch/infrastructure-config';\nimport { SecurityConfig } from '../models/security-options';\n\nexport const authInterceptor: HttpInterceptorFn = (req, next) => {\n const config = inject(ConfigService);\n const security = inject(SecurityService);\n const requestUrl = req.url ?? '';\n\n if (!requestUrl) {\n return next(req);\n }\n\n const token = security.token();\n if (!token) return next(req);\n\n const keys = config.get<SecurityConfig>('SECURITY')?.SECURE_API_KEYS ?? [];\n\n const isSecure = keys\n .map(key => config.get<string>(key))\n .filter((url): url is string => !!url)\n .some(url => requestUrl.startsWith(url));\n\n if (isSecure) {\n return next(req.clone({\n setHeaders: { Authorization: `Bearer ${token}` },\n }));\n }\n\n return next(req);\n};\n","import { isPlatformBrowser } from '@angular/common';\nimport { Component, inject, OnInit, PLATFORM_ID } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { OAuthService } from 'angular-oauth2-oidc';\n\nimport { safeRel, stateDecodeFromUrlParam } from '../utils/oauth-state.util';\n\n/**\n * Landing component for the OAuth callback route.\n *\n * The APP_INITIALIZER (SecurityService.init) should have already\n * exchanged the authorization code. If it failed for any reason\n * this component retries the code exchange as a fallback.\n */\n@Component({\n selector: 'eac-oidc-callback',\n standalone: true,\n template: '',\n styles: '',\n})\nexport class OidcCallback implements OnInit {\n private readonly oauth = inject(OAuthService);\n private readonly router = inject(Router);\n private readonly pid = inject(PLATFORM_ID);\n\n async ngOnInit(): Promise<void> {\n if (!isPlatformBrowser(this.pid)) return;\n\n // If init() already exchanged the code, just navigate.\n // Otherwise retry the exchange here (fallback).\n if (!this.oauth.hasValidAccessToken()) {\n try {\n await this.oauth.loadDiscoveryDocument();\n await this.oauth.tryLoginCodeFlow();\n } catch (err) {\n console.error('[SECURITY] OidcCallback code exchange failed:', err);\n }\n }\n\n if (this.oauth.hasValidAccessToken()) {\n const obj = (stateDecodeFromUrlParam(this.oauth.state) ?? {}) as Record<string, unknown>;\n const rel = safeRel(obj['rel'] || '/');\n window.history.replaceState({}, document.title, '/');\n this.router.navigateByUrl(rel, { replaceUrl: true });\n } else {\n console.warn('[SECURITY] No valid token after callback, redirecting home');\n this.router.navigateByUrl('/', { replaceUrl: true });\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eac-arch/infrastructure-security",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/common": "^21.1.0",
|
|
6
|
-
"@angular/core": "^21.1.0"
|
|
6
|
+
"@angular/core": "^21.1.0",
|
|
7
|
+
"@angular/router": "^21.1.0",
|
|
8
|
+
"angular-oauth2-oidc": ">=17.0.0"
|
|
7
9
|
},
|
|
8
10
|
"dependencies": {
|
|
9
|
-
"tslib": "^2.3.0"
|
|
11
|
+
"tslib": "^2.3.0",
|
|
12
|
+
"@eac-arch/infrastructure-config": "1.0.6"
|
|
10
13
|
},
|
|
11
14
|
"sideEffects": false,
|
|
12
15
|
"module": "fesm2022/eac-arch-infrastructure-security.mjs",
|
|
@@ -20,4 +23,4 @@
|
|
|
20
23
|
"default": "./fesm2022/eac-arch-infrastructure-security.mjs"
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
|
-
}
|
|
26
|
+
}
|
|
@@ -1,8 +1,99 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
+
import { Signal, EnvironmentProviders, OnInit } from '@angular/core';
|
|
3
|
+
import { Router, CanActivateFn } from '@angular/router';
|
|
4
|
+
import { OAuthService } from 'angular-oauth2-oidc';
|
|
5
|
+
import { HttpInterceptorFn } from '@angular/common/http';
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
interface IdpConfig {
|
|
8
|
+
ISSUER: string;
|
|
9
|
+
CLIENT_ID: string;
|
|
10
|
+
REDIRECT_URI?: string;
|
|
11
|
+
POST_LOGOUT_REDIRECT_URI?: string;
|
|
12
|
+
RESPONSE_TYPE?: 'code' | 'token';
|
|
13
|
+
SCOPE?: string;
|
|
14
|
+
TOKEN_ENDPOINT?: string;
|
|
15
|
+
STRICT_DISCOVERY_DOCUMENT_VALIDATION?: boolean;
|
|
16
|
+
SHOW_DEBUG_INFORMATION?: boolean;
|
|
17
|
+
REQUIRE_HTTPS?: boolean;
|
|
18
|
+
GRAPH_SCOPES?: string;
|
|
6
19
|
}
|
|
7
20
|
|
|
8
|
-
|
|
21
|
+
interface UserProfile {
|
|
22
|
+
sub: string | null;
|
|
23
|
+
displayName: string;
|
|
24
|
+
firstName?: string | null;
|
|
25
|
+
lastName?: string | null;
|
|
26
|
+
email?: string | null;
|
|
27
|
+
username?: string | null;
|
|
28
|
+
pictureUrl?: string | null;
|
|
29
|
+
roles?: string[];
|
|
30
|
+
raw?: unknown;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface SecurityConfig {
|
|
34
|
+
SECURE_API_KEYS?: string[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare class SecurityService {
|
|
38
|
+
private readonly isBrowser;
|
|
39
|
+
private readonly oauth;
|
|
40
|
+
private readonly router;
|
|
41
|
+
private readonly getIdpConfig;
|
|
42
|
+
private readonly oauthEvt;
|
|
43
|
+
private readonly accessToken;
|
|
44
|
+
private readonly payload;
|
|
45
|
+
private readonly tokenManager;
|
|
46
|
+
private readonly profileResolver;
|
|
47
|
+
readonly isLoggedIn: Signal<boolean>;
|
|
48
|
+
readonly profile: Signal<UserProfile | null>;
|
|
49
|
+
readonly rolesSignal: Signal<string[]>;
|
|
50
|
+
readonly scopesSignal: Signal<string[]>;
|
|
51
|
+
constructor(isBrowser: boolean, oauth: OAuthService, router: Router, getIdpConfig: () => IdpConfig | null, destroyRef: {
|
|
52
|
+
onDestroy: (fn: () => void) => void;
|
|
53
|
+
});
|
|
54
|
+
init(): Promise<void>;
|
|
55
|
+
token(): string | null;
|
|
56
|
+
isAuthenticated(): boolean;
|
|
57
|
+
roles(): string[];
|
|
58
|
+
scopes(): string[];
|
|
59
|
+
hasAnyRole(required: string[]): boolean;
|
|
60
|
+
hasAllRoles(required: string[]): boolean;
|
|
61
|
+
hasAnyScope(required: string[]): boolean;
|
|
62
|
+
login(target?: string): void;
|
|
63
|
+
logout(): void;
|
|
64
|
+
private buildAuthConfig;
|
|
65
|
+
private resolveRedirectUri;
|
|
66
|
+
private subscribeToOAuthEvents;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
declare function provideSecurity(): EnvironmentProviders;
|
|
70
|
+
|
|
71
|
+
declare const authGuard: CanActivateFn;
|
|
72
|
+
|
|
73
|
+
declare const authzGuard: CanActivateFn;
|
|
74
|
+
|
|
75
|
+
declare const authInterceptor: HttpInterceptorFn;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Landing component for the OAuth callback route.
|
|
79
|
+
*
|
|
80
|
+
* The APP_INITIALIZER (SecurityService.init) should have already
|
|
81
|
+
* exchanged the authorization code. If it failed for any reason
|
|
82
|
+
* this component retries the code exchange as a fallback.
|
|
83
|
+
*/
|
|
84
|
+
declare class OidcCallback implements OnInit {
|
|
85
|
+
private readonly oauth;
|
|
86
|
+
private readonly router;
|
|
87
|
+
private readonly pid;
|
|
88
|
+
ngOnInit(): Promise<void>;
|
|
89
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<OidcCallback, never>;
|
|
90
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<OidcCallback, "eac-oidc-callback", never, {}, {}, never, never, true, never>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
declare function stateEncode(obj: unknown): string;
|
|
94
|
+
declare function stateDecode(s: string): unknown | null;
|
|
95
|
+
declare function stateDecodeFromUrlParam(s?: string | null): Record<string, unknown> | null;
|
|
96
|
+
declare function safeRel(rel: unknown): string;
|
|
97
|
+
|
|
98
|
+
export { OidcCallback, SecurityService, authGuard, authInterceptor, authzGuard, provideSecurity, safeRel, stateDecode, stateDecodeFromUrlParam, stateEncode };
|
|
99
|
+
export type { IdpConfig, SecurityConfig, UserProfile };
|