@masterteam/gateway-auth 0.0.17 → 0.0.19
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.
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { firstValueFrom, EMPTY, of, defer, from, isObservable, map, tap as tap$1, shareReplay, finalize, catchError as catchError$1, throwError, switchMap as switchMap$1 } from 'rxjs';
|
|
1
2
|
import { HttpClient, HttpContextToken, HttpBackend } from '@angular/common/http';
|
|
2
3
|
import * as i0 from '@angular/core';
|
|
3
4
|
import { InjectionToken, inject, Injectable, computed, input, ChangeDetectionStrategy, Component, signal, effect } from '@angular/core';
|
|
4
|
-
import { EMPTY, of, isObservable, from, firstValueFrom, map, tap as tap$1, shareReplay, finalize, catchError as catchError$1, throwError, switchMap } from 'rxjs';
|
|
5
5
|
import { Action, Selector, State, Store, select } from '@ngxs/store';
|
|
6
6
|
import { Router, ActivatedRoute } from '@angular/router';
|
|
7
7
|
import { ModalService } from '@masterteam/components/modal';
|
|
8
|
-
import { mergeMap, catchError, tap } from 'rxjs/operators';
|
|
8
|
+
import { switchMap, mergeMap, catchError, tap } from 'rxjs/operators';
|
|
9
9
|
import * as i2 from '@angular/common';
|
|
10
10
|
import { NgTemplateOutlet, CommonModule } from '@angular/common';
|
|
11
11
|
import * as i1 from '@angular/forms';
|
|
@@ -66,6 +66,48 @@ function resolveApplicationCodeOption(applicationCode) {
|
|
|
66
66
|
const normalized = value?.trim();
|
|
67
67
|
return normalized ? normalized : null;
|
|
68
68
|
}
|
|
69
|
+
const applicationContextCache = new Map();
|
|
70
|
+
function buildApplicationContextUrl(applicationApiBaseUrl) {
|
|
71
|
+
if (!applicationApiBaseUrl) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const base = normalizeGatewayBase(applicationApiBaseUrl);
|
|
75
|
+
if (!base) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const path = GATEWAY_AUTH_ENDPOINTS.applicationContext;
|
|
79
|
+
if (/\/api\/?$/i.test(base)) {
|
|
80
|
+
return `${base.replace(/\/+$/, '')}/${path}`;
|
|
81
|
+
}
|
|
82
|
+
return `${base}/api/${path}`;
|
|
83
|
+
}
|
|
84
|
+
function clearApplicationContextCache(applicationApiBaseUrl) {
|
|
85
|
+
if (applicationApiBaseUrl) {
|
|
86
|
+
applicationContextCache.delete(applicationApiBaseUrl);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
applicationContextCache.clear();
|
|
90
|
+
}
|
|
91
|
+
async function fetchApplicationContextCode(http, applicationApiBaseUrl) {
|
|
92
|
+
const url = buildApplicationContextUrl(applicationApiBaseUrl);
|
|
93
|
+
if (!url || !applicationApiBaseUrl) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const cacheKey = applicationApiBaseUrl;
|
|
97
|
+
const cached = applicationContextCache.get(cacheKey);
|
|
98
|
+
if (cached) {
|
|
99
|
+
return cached;
|
|
100
|
+
}
|
|
101
|
+
const promise = firstValueFrom(http.get(url))
|
|
102
|
+
.then((response) => response?.data?.applicationCode?.trim() || null)
|
|
103
|
+
.catch(() => null);
|
|
104
|
+
applicationContextCache.set(cacheKey, promise);
|
|
105
|
+
const code = await promise;
|
|
106
|
+
if (!code) {
|
|
107
|
+
applicationContextCache.delete(cacheKey);
|
|
108
|
+
}
|
|
109
|
+
return code;
|
|
110
|
+
}
|
|
69
111
|
function resolveGatewayDeviceToken(deviceToken) {
|
|
70
112
|
const configuredToken = typeof deviceToken === 'function' ? deviceToken() : deviceToken;
|
|
71
113
|
const normalizedConfiguredToken = configuredToken?.trim();
|
|
@@ -438,6 +480,7 @@ const AUTH_STATE_DEFAULTS = {
|
|
|
438
480
|
applicationsLoading: false,
|
|
439
481
|
appSessions: {},
|
|
440
482
|
appLaunchLoading: {},
|
|
483
|
+
activeApplicationCode: null,
|
|
441
484
|
};
|
|
442
485
|
function pruneRateLimit(rateLimit) {
|
|
443
486
|
if (!rateLimit) {
|
|
@@ -482,6 +525,7 @@ function sanitizePersistedAuthState(obj) {
|
|
|
482
525
|
applicationsLoading: false,
|
|
483
526
|
appSessions: pruneAppSessions(obj?.appSessions ?? null),
|
|
484
527
|
appLaunchLoading: {},
|
|
528
|
+
activeApplicationCode: obj?.activeApplicationCode ?? null,
|
|
485
529
|
};
|
|
486
530
|
}
|
|
487
531
|
|
|
@@ -557,6 +601,9 @@ let GatewayAuthState = class GatewayAuthState {
|
|
|
557
601
|
static appLaunchLoading(state) {
|
|
558
602
|
return state.appLaunchLoading;
|
|
559
603
|
}
|
|
604
|
+
static activeApplicationCode(state) {
|
|
605
|
+
return state.activeApplicationCode;
|
|
606
|
+
}
|
|
560
607
|
login(ctx, action) {
|
|
561
608
|
if (this.isRateLimitActive(ctx, 'login')) {
|
|
562
609
|
return EMPTY;
|
|
@@ -567,26 +614,37 @@ let GatewayAuthState = class GatewayAuthState {
|
|
|
567
614
|
pendingMfa: null,
|
|
568
615
|
twoFactorRequired: false,
|
|
569
616
|
});
|
|
570
|
-
|
|
571
|
-
resolveApplicationCodeOption(this.options.applicationCode) ??
|
|
572
|
-
undefined;
|
|
573
|
-
return this.http
|
|
617
|
+
return this.resolveApplicationCode$(action.payload.applicationCode).pipe(switchMap((applicationCode) => this.http
|
|
574
618
|
.post(this.gatewayAuthMutationUrl(GATEWAY_AUTH_ENDPOINTS.login), {
|
|
575
619
|
userName: action.payload.userName,
|
|
576
620
|
password: action.payload.password,
|
|
577
|
-
applicationCode,
|
|
621
|
+
applicationCode: applicationCode ?? undefined,
|
|
578
622
|
isEncrypted: action.payload.isEncrypted ?? false,
|
|
579
623
|
deviceToken: action.payload.deviceToken || this.deviceToken,
|
|
580
624
|
recaptchaToken: action.payload.recaptchaToken,
|
|
581
625
|
recaptchaAction: action.payload.recaptchaAction,
|
|
582
626
|
})
|
|
583
|
-
.pipe(mergeMap((response) => this.handleLoginResponse(ctx, response.data)), catchError((error) => {
|
|
627
|
+
.pipe(mergeMap((response) => this.handleLoginResponse(ctx, response.data, applicationCode)), catchError((error) => {
|
|
584
628
|
if (this.handleRateLimit(ctx, error, 'login')) {
|
|
585
629
|
return of(null);
|
|
586
630
|
}
|
|
587
631
|
ctx.dispatch(new LoginFailure(getGatewayErrorMessage(error, 'Login failed')));
|
|
588
632
|
return of(null);
|
|
589
|
-
}));
|
|
633
|
+
}))));
|
|
634
|
+
}
|
|
635
|
+
resolveApplicationCode$(explicitCode) {
|
|
636
|
+
if (explicitCode) {
|
|
637
|
+
return of(explicitCode);
|
|
638
|
+
}
|
|
639
|
+
const configured = resolveApplicationCodeOption(this.options.applicationCode);
|
|
640
|
+
if (configured) {
|
|
641
|
+
return of(configured);
|
|
642
|
+
}
|
|
643
|
+
const applicationApiBaseUrl = this.options.getApplicationApiBaseUrl?.();
|
|
644
|
+
if (!applicationApiBaseUrl) {
|
|
645
|
+
return of(null);
|
|
646
|
+
}
|
|
647
|
+
return defer(() => from(fetchApplicationContextCode(this.http, applicationApiBaseUrl)));
|
|
590
648
|
}
|
|
591
649
|
verifyMfa(ctx, action) {
|
|
592
650
|
const pendingMfa = ctx.getState().pendingMfa;
|
|
@@ -736,6 +794,7 @@ let GatewayAuthState = class GatewayAuthState {
|
|
|
736
794
|
applications: [],
|
|
737
795
|
appSessions: {},
|
|
738
796
|
appLaunchLoading: {},
|
|
797
|
+
activeApplicationCode: null,
|
|
739
798
|
});
|
|
740
799
|
}
|
|
741
800
|
logout(ctx, action) {
|
|
@@ -871,6 +930,7 @@ let GatewayAuthState = class GatewayAuthState {
|
|
|
871
930
|
[data.applicationCode]: session,
|
|
872
931
|
},
|
|
873
932
|
appLaunchLoading: this.removeLoadingFlag(ctx, code),
|
|
933
|
+
activeApplicationCode: data.applicationCode,
|
|
874
934
|
});
|
|
875
935
|
if (action.navigate && session.launchUrl) {
|
|
876
936
|
try {
|
|
@@ -918,10 +978,20 @@ let GatewayAuthState = class GatewayAuthState {
|
|
|
918
978
|
clearAppSession(ctx, action) {
|
|
919
979
|
const sessions = { ...ctx.getState().appSessions };
|
|
920
980
|
delete sessions[action.applicationCode];
|
|
921
|
-
ctx.
|
|
981
|
+
const state = ctx.getState();
|
|
982
|
+
ctx.patchState({
|
|
983
|
+
appSessions: sessions,
|
|
984
|
+
activeApplicationCode: state.activeApplicationCode === action.applicationCode
|
|
985
|
+
? null
|
|
986
|
+
: state.activeApplicationCode,
|
|
987
|
+
});
|
|
922
988
|
}
|
|
923
989
|
clearAllAppSessions(ctx) {
|
|
924
|
-
ctx.patchState({
|
|
990
|
+
ctx.patchState({
|
|
991
|
+
appSessions: {},
|
|
992
|
+
applications: [],
|
|
993
|
+
activeApplicationCode: null,
|
|
994
|
+
});
|
|
925
995
|
}
|
|
926
996
|
removeLoadingFlag(ctx, code) {
|
|
927
997
|
const next = { ...ctx.getState().appLaunchLoading };
|
|
@@ -955,7 +1025,7 @@ let GatewayAuthState = class GatewayAuthState {
|
|
|
955
1025
|
}));
|
|
956
1026
|
return true;
|
|
957
1027
|
}
|
|
958
|
-
handleLoginResponse(ctx, session) {
|
|
1028
|
+
handleLoginResponse(ctx, session, resolvedApplicationCode) {
|
|
959
1029
|
if (session.requiresTwoFactor) {
|
|
960
1030
|
ctx.patchState({
|
|
961
1031
|
user: null,
|
|
@@ -993,18 +1063,20 @@ let GatewayAuthState = class GatewayAuthState {
|
|
|
993
1063
|
twoFactorRequired: false,
|
|
994
1064
|
appSessions: {},
|
|
995
1065
|
appLaunchLoading: {},
|
|
1066
|
+
activeApplicationCode: null,
|
|
996
1067
|
});
|
|
997
|
-
return this.toObservable(this.options.afterLogin?.(session, ctx)).pipe(mergeMap(() => this.maybeAutoLaunchApplication(ctx)), tap(() => {
|
|
1068
|
+
return this.toObservable(this.options.afterLogin?.(session, ctx)).pipe(mergeMap(() => this.maybeAutoLaunchApplication(ctx, resolvedApplicationCode)), tap(() => {
|
|
998
1069
|
this.router.navigateByUrl(this.resolveAuthenticatedRoute(), {
|
|
999
1070
|
replaceUrl: true,
|
|
1000
1071
|
});
|
|
1001
1072
|
}));
|
|
1002
1073
|
}
|
|
1003
|
-
maybeAutoLaunchApplication(ctx) {
|
|
1074
|
+
maybeAutoLaunchApplication(ctx, resolvedApplicationCode) {
|
|
1004
1075
|
if (!this.options.autoLaunchApplicationOnLogin) {
|
|
1005
1076
|
return of(null);
|
|
1006
1077
|
}
|
|
1007
|
-
const code =
|
|
1078
|
+
const code = resolvedApplicationCode ??
|
|
1079
|
+
resolveApplicationCodeOption(this.options.applicationCode);
|
|
1008
1080
|
if (!code) {
|
|
1009
1081
|
return of(null);
|
|
1010
1082
|
}
|
|
@@ -1185,6 +1257,9 @@ __decorate([
|
|
|
1185
1257
|
__decorate([
|
|
1186
1258
|
Selector()
|
|
1187
1259
|
], GatewayAuthState, "appLaunchLoading", null);
|
|
1260
|
+
__decorate([
|
|
1261
|
+
Selector()
|
|
1262
|
+
], GatewayAuthState, "activeApplicationCode", null);
|
|
1188
1263
|
GatewayAuthState = __decorate([
|
|
1189
1264
|
State({
|
|
1190
1265
|
name: 'auth',
|
|
@@ -1217,6 +1292,7 @@ class GatewayAuthFacade {
|
|
|
1217
1292
|
applicationsLoading = select(GatewayAuthState.applicationsLoading);
|
|
1218
1293
|
appSessions = select(GatewayAuthState.appSessions);
|
|
1219
1294
|
appLaunchLoading = select(GatewayAuthState.appLaunchLoading);
|
|
1295
|
+
activeApplicationCode = select(GatewayAuthState.activeApplicationCode);
|
|
1220
1296
|
hasError = computed(() => this.error() !== null, ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
|
|
1221
1297
|
isReady = computed(() => !this.loading() && this.error() === null, ...(ngDevMode ? [{ debugName: "isReady" }] : /* istanbul ignore next */ []));
|
|
1222
1298
|
userDisplayName = computed(() => this.user()?.user?.displayName || '', ...(ngDevMode ? [{ debugName: "userDisplayName" }] : /* istanbul ignore next */ []));
|
|
@@ -1523,8 +1599,14 @@ const prepareRequest = (req, token, markRetried, baseUrl) => {
|
|
|
1523
1599
|
}
|
|
1524
1600
|
return modifiedReq;
|
|
1525
1601
|
};
|
|
1526
|
-
const
|
|
1527
|
-
|
|
1602
|
+
const urlMatchesBase = (url, baseUrl) => {
|
|
1603
|
+
if (!baseUrl || !isAbsoluteUrl(url)) {
|
|
1604
|
+
return false;
|
|
1605
|
+
}
|
|
1606
|
+
return url.startsWith(normalizeBase(baseUrl));
|
|
1607
|
+
};
|
|
1608
|
+
const resolveRequestScope = (req, options, auth, gatewayApiBaseUrl) => {
|
|
1609
|
+
// 1. Per-request override wins.
|
|
1528
1610
|
const explicitCode = options.resolveApplicationCodeForRequest?.(req);
|
|
1529
1611
|
if (explicitCode) {
|
|
1530
1612
|
const session = auth.getAppSession(explicitCode);
|
|
@@ -1532,13 +1614,21 @@ const resolveRequestScope = (req, options, auth) => {
|
|
|
1532
1614
|
scope: 'application',
|
|
1533
1615
|
applicationCode: explicitCode,
|
|
1534
1616
|
session,
|
|
1535
|
-
useGatewayBaseUrl,
|
|
1617
|
+
useGatewayBaseUrl: false,
|
|
1536
1618
|
};
|
|
1537
1619
|
}
|
|
1538
|
-
|
|
1620
|
+
// 2. Caller hint via request context.
|
|
1621
|
+
if (options.shouldUseGatewayApiBaseUrl?.(req)) {
|
|
1539
1622
|
return { scope: 'gateway', useGatewayBaseUrl: true };
|
|
1540
1623
|
}
|
|
1541
|
-
|
|
1624
|
+
// 3. Absolute URL pointing at the Gateway base must use GatewaySession.
|
|
1625
|
+
// Covers /api/auth/*, /api/auth/me/applications, /api/applications/{code}/launch.
|
|
1626
|
+
if (urlMatchesBase(req.url, gatewayApiBaseUrl)) {
|
|
1627
|
+
return { scope: 'gateway', useGatewayBaseUrl: false };
|
|
1628
|
+
}
|
|
1629
|
+
// 4. Otherwise treat as app scope when a session exists.
|
|
1630
|
+
const defaultCode = resolveApplicationCodeOption(options.applicationCode) ??
|
|
1631
|
+
auth.activeApplicationCode();
|
|
1542
1632
|
if (defaultCode) {
|
|
1543
1633
|
const session = auth.getAppSession(defaultCode);
|
|
1544
1634
|
if (session) {
|
|
@@ -1546,11 +1636,12 @@ const resolveRequestScope = (req, options, auth) => {
|
|
|
1546
1636
|
scope: 'application',
|
|
1547
1637
|
applicationCode: defaultCode,
|
|
1548
1638
|
session,
|
|
1549
|
-
useGatewayBaseUrl,
|
|
1639
|
+
useGatewayBaseUrl: false,
|
|
1550
1640
|
};
|
|
1551
1641
|
}
|
|
1552
1642
|
}
|
|
1553
|
-
|
|
1643
|
+
// 5. No app session known yet — fall back to GatewaySession.
|
|
1644
|
+
return { scope: 'gateway', useGatewayBaseUrl: false };
|
|
1554
1645
|
};
|
|
1555
1646
|
const gatewayAuthInterceptor = (req, next) => {
|
|
1556
1647
|
if (isAssetRequest(req.url)) {
|
|
@@ -1565,7 +1656,7 @@ const gatewayAuthInterceptor = (req, next) => {
|
|
|
1565
1656
|
const refreshSkewMs = resolveAccessTokenRefreshSkewMs(options.accessTokenRefreshSkewMs);
|
|
1566
1657
|
const isAuthRequest = isGatewayAuthRequestUrl(req.url, gatewayApiBaseUrl);
|
|
1567
1658
|
const alreadyRetried = req.context.get(GATEWAY_AUTH_RETRY_CONTEXT);
|
|
1568
|
-
const resolved = resolveRequestScope(req, options, auth);
|
|
1659
|
+
const resolved = resolveRequestScope(req, options, auth, gatewayApiBaseUrl);
|
|
1569
1660
|
const baseUrl = resolved.useGatewayBaseUrl
|
|
1570
1661
|
? gatewayApiBaseUrl
|
|
1571
1662
|
: appApiBaseUrl;
|
|
@@ -1607,7 +1698,7 @@ const gatewayAuthInterceptor = (req, next) => {
|
|
|
1607
1698
|
auth.logout();
|
|
1608
1699
|
}
|
|
1609
1700
|
return throwError(() => refreshError);
|
|
1610
|
-
}), switchMap((tokens) => next(prepareRequest(req, tokens.accessToken, true, baseUrl))));
|
|
1701
|
+
}), switchMap$1((tokens) => next(prepareRequest(req, tokens.accessToken, true, baseUrl))));
|
|
1611
1702
|
}
|
|
1612
1703
|
return next(prepareRequest(req, tokenToAttach, false, baseUrl)).pipe(catchError$1((error) => {
|
|
1613
1704
|
if (error?.status !== 401 || isAuthRequest || alreadyRetried) {
|
|
@@ -1640,7 +1731,7 @@ const gatewayAuthInterceptor = (req, next) => {
|
|
|
1640
1731
|
auth.logout();
|
|
1641
1732
|
}
|
|
1642
1733
|
return throwError(() => refreshError);
|
|
1643
|
-
}), switchMap((tokens) => next(prepareRequest(req, tokens.accessToken, true, baseUrl))));
|
|
1734
|
+
}), switchMap$1((tokens) => next(prepareRequest(req, tokens.accessToken, true, baseUrl))));
|
|
1644
1735
|
}));
|
|
1645
1736
|
};
|
|
1646
1737
|
|
|
@@ -1989,5 +2080,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1989
2080
|
* Generated bundle index. Do not edit.
|
|
1990
2081
|
*/
|
|
1991
2082
|
|
|
1992
|
-
export { AUTH_STATE_DEFAULTS, ClearAllAppSessions, ClearAppSession, ClearError, ClearPendingMfa, ClearRateLimit, ExchangeSsoCode, GATEWAY_AUTH_ACCESS_TOKEN_REFRESH_SKEW_MS, GATEWAY_AUTH_DEVICE_TOKEN, GATEWAY_AUTH_DEVICE_TOKEN_STORAGE_KEY, GATEWAY_AUTH_ENDPOINTS, GATEWAY_AUTH_NGSW_BYPASS_PARAM, GATEWAY_AUTH_OPTIONS, GATEWAY_AUTH_RETRY_CONTEXT, GATEWAY_RATE_LIMIT_ERROR_CODE, GATEWAY_RATE_LIMIT_STATUS, GatewayAuthFacade, GatewayAuthState, GatewayLoginPage, GatewayMfa, GatewaySsoButtons, GatewaySsoCallback, GatewaySsoSession, LaunchApplication, LoadApplications, LoadSsoProviders, Login, LoginFailure, LoginSuccess, Logout, ResendMfa, SetAppSession, SetApplications, SetRateLimit, StartSso, UpdateAppTokens, UpdateTokens, UpdateUserData, VerifyMfa, buildGatewayUrl, buildSsoStartUrl, createSecureClientState, extractGatewayRateLimitInfo, gatewayAuthInterceptor, getGatewayErrorMessage, hasGatewayTokens, isExpired, isGatewayAuthRequestUrl, mapGatewayTokens, mapGatewayUser, normalizeGatewayBase, readPersistedGatewayAuthTokens, resolveAccessTokenRefreshSkewMs, resolveApiDateValue, resolveApplicationCodeOption, resolveGatewayAuthPath, resolveGatewayDeviceToken, sanitizePersistedAuthState, withGatewayAuthNgswBypass };
|
|
2083
|
+
export { AUTH_STATE_DEFAULTS, ClearAllAppSessions, ClearAppSession, ClearError, ClearPendingMfa, ClearRateLimit, ExchangeSsoCode, GATEWAY_AUTH_ACCESS_TOKEN_REFRESH_SKEW_MS, GATEWAY_AUTH_DEVICE_TOKEN, GATEWAY_AUTH_DEVICE_TOKEN_STORAGE_KEY, GATEWAY_AUTH_ENDPOINTS, GATEWAY_AUTH_NGSW_BYPASS_PARAM, GATEWAY_AUTH_OPTIONS, GATEWAY_AUTH_RETRY_CONTEXT, GATEWAY_RATE_LIMIT_ERROR_CODE, GATEWAY_RATE_LIMIT_STATUS, GatewayAuthFacade, GatewayAuthState, GatewayLoginPage, GatewayMfa, GatewaySsoButtons, GatewaySsoCallback, GatewaySsoSession, LaunchApplication, LoadApplications, LoadSsoProviders, Login, LoginFailure, LoginSuccess, Logout, ResendMfa, SetAppSession, SetApplications, SetRateLimit, StartSso, UpdateAppTokens, UpdateTokens, UpdateUserData, VerifyMfa, buildApplicationContextUrl, buildGatewayUrl, buildSsoStartUrl, clearApplicationContextCache, createSecureClientState, extractGatewayRateLimitInfo, fetchApplicationContextCode, gatewayAuthInterceptor, getGatewayErrorMessage, hasGatewayTokens, isExpired, isGatewayAuthRequestUrl, mapGatewayTokens, mapGatewayUser, normalizeGatewayBase, readPersistedGatewayAuthTokens, resolveAccessTokenRefreshSkewMs, resolveApiDateValue, resolveApplicationCodeOption, resolveGatewayAuthPath, resolveGatewayDeviceToken, sanitizePersistedAuthState, withGatewayAuthNgswBypass };
|
|
1993
2084
|
//# sourceMappingURL=masterteam-gateway-auth.mjs.map
|