@masterteam/gateway-auth 0.0.24 → 0.0.26

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,4 +1,4 @@
1
- import { firstValueFrom, EMPTY, of, defer, from, isObservable, map, tap as tap$1, catchError as catchError$1, throwError, switchMap as switchMap$1 } from 'rxjs';
1
+ import { firstValueFrom, EMPTY, of, defer, from, isObservable, map, tap as tap$1, throwError, catchError as catchError$1, switchMap as switchMap$1 } from 'rxjs';
2
2
  import { HttpClient, HttpContextToken, HttpBackend, HttpResponse } from '@angular/common/http';
3
3
  import * as i0 from '@angular/core';
4
4
  import { InjectionToken, inject, Injectable, computed, input, ChangeDetectionStrategy, Component, signal, effect } from '@angular/core';
@@ -956,6 +956,7 @@ let GatewayAuthState = class GatewayAuthState {
956
956
  ...ctx.getState().appSessions,
957
957
  [action.session.applicationCode]: action.session,
958
958
  },
959
+ activeApplicationCode: action.session.applicationCode,
959
960
  });
960
961
  }
961
962
  updateAppTokens(ctx, action) {
@@ -1434,25 +1435,64 @@ const normalizePath = (path) => {
1434
1435
  const withoutQuery = trimmed.split('?')[0] || '';
1435
1436
  return withoutQuery.startsWith('api/') ? withoutQuery.slice(4) : withoutQuery;
1436
1437
  };
1437
- function resolveGatewayAuthPath(url, gatewayApiBaseUrl) {
1438
+ const getBasePath = (baseUrl) => {
1439
+ if (!baseUrl) {
1440
+ return '';
1441
+ }
1442
+ try {
1443
+ return new URL(baseUrl).pathname;
1444
+ }
1445
+ catch {
1446
+ return baseUrl.startsWith('/') ? baseUrl : '';
1447
+ }
1448
+ };
1449
+ const stripBasePath = (path, baseUrl) => {
1450
+ const basePath = getBasePath(baseUrl).replace(/^\/+/, '').replace(/\/+$/, '');
1451
+ const normalizedPath = path.replace(/^\/+/, '');
1452
+ if (basePath &&
1453
+ (normalizedPath === basePath || normalizedPath.startsWith(`${basePath}/`))) {
1454
+ return normalizedPath.slice(basePath.length).replace(/^\/+/, '');
1455
+ }
1456
+ return path;
1457
+ };
1458
+ const resolveRequestPath = (url, baseUrl) => {
1459
+ let path = url;
1438
1460
  if (isAbsoluteUrl(url)) {
1439
1461
  try {
1440
- return normalizePath(new URL(url).pathname);
1462
+ path = new URL(url).pathname;
1441
1463
  }
1442
1464
  catch {
1443
- return normalizePath(url);
1465
+ path = url;
1444
1466
  }
1445
1467
  }
1446
- if (gatewayApiBaseUrl && url.startsWith(gatewayApiBaseUrl)) {
1447
- return normalizePath(url.slice(gatewayApiBaseUrl.length));
1468
+ else if (baseUrl && url.startsWith(baseUrl)) {
1469
+ path = url.slice(baseUrl.length);
1448
1470
  }
1449
- return normalizePath(url);
1471
+ return normalizePath(stripBasePath(path, baseUrl));
1472
+ };
1473
+ function resolveGatewayAuthPath(url, gatewayApiBaseUrl) {
1474
+ return resolveRequestPath(url, gatewayApiBaseUrl);
1450
1475
  }
1451
1476
  function isGatewayAuthRequestUrl(url, gatewayApiBaseUrl) {
1452
1477
  const path = resolveGatewayAuthPath(url, gatewayApiBaseUrl).toLowerCase();
1453
1478
  return (GATEWAY_AUTH_ENDPOINT_PATHS.has(path) ||
1454
1479
  GATEWAY_AUTH_ENDPOINT_PREFIXES.some((prefix) => path.startsWith(prefix)));
1455
1480
  }
1481
+ const GATEWAY_APPLICATION_LAUNCH_PATH_PATTERN = /^applications\/[^/]+\/launch$/i;
1482
+ const isGatewayEndpointRequestUrl = (url, gatewayApiBaseUrl) => {
1483
+ const path = resolveGatewayAuthPath(url, gatewayApiBaseUrl).toLowerCase();
1484
+ return (path.startsWith('auth/') ||
1485
+ GATEWAY_APPLICATION_LAUNCH_PATH_PATTERN.test(path));
1486
+ };
1487
+ const isApplicationContextRequestUrl = (url, applicationApiBaseUrl) => resolveRequestPath(url, applicationApiBaseUrl).toLowerCase() ===
1488
+ GATEWAY_AUTH_ENDPOINTS.applicationContext;
1489
+ class GatewaySessionDeadError extends Error {
1490
+ constructor() {
1491
+ super('GATEWAY_SESSION_DEAD');
1492
+ }
1493
+ }
1494
+ const isGatewaySessionDeadError = (error) => error instanceof GatewaySessionDeadError;
1495
+ const isUnauthorizedError = (error) => error?.status === 401;
1456
1496
  const getBrowserRefreshLock = () => {
1457
1497
  if (typeof navigator === 'undefined') {
1458
1498
  return null;
@@ -1601,6 +1641,138 @@ const refreshAccessToken = (context, refreshToken) => {
1601
1641
  return promise;
1602
1642
  };
1603
1643
  const refreshTokens$ = (context, refreshToken) => from(refreshAccessToken(context, refreshToken));
1644
+ const inflightRelaunchByCode = new Map();
1645
+ const resolveRelaunchReturnUrl = (options, applicationCode) => {
1646
+ const fromOption = options.getApplicationLaunchReturnUrl?.(applicationCode);
1647
+ const trimmed = fromOption?.trim();
1648
+ if (trimmed) {
1649
+ return trimmed;
1650
+ }
1651
+ if (typeof window !== 'undefined' && window.location?.origin) {
1652
+ return window.location.origin;
1653
+ }
1654
+ return null;
1655
+ };
1656
+ const executeRelaunch = (context, options) => {
1657
+ const code = context.applicationCode;
1658
+ if (!code) {
1659
+ return Promise.reject(new Error('Cannot re-launch without applicationCode'));
1660
+ }
1661
+ const lockKey = `masterteam-gateway-auth-relaunch:${code}`;
1662
+ return runWithRefreshLock(lockKey, async () => {
1663
+ // If another flow/tab already refreshed the app session, use it.
1664
+ const existingSession = context.auth.getAppSession(code);
1665
+ if (existingSession &&
1666
+ existingSession.accessToken &&
1667
+ !isExpired(existingSession.accessTokenExpiresAt, context.refreshSkewMs)) {
1668
+ return {
1669
+ accessToken: existingSession.accessToken,
1670
+ accessTokenExpiresAt: {
1671
+ actualValue: existingSession.accessTokenExpiresAt ?? '',
1672
+ },
1673
+ refreshToken: existingSession.refreshToken,
1674
+ refreshTokenExpiresAt: {
1675
+ actualValue: existingSession.refreshTokenExpiresAt ?? '',
1676
+ },
1677
+ };
1678
+ }
1679
+ const ensureGatewayAccessToken = async (forceRefresh = false) => {
1680
+ const gatewayAccessToken = context.auth.token();
1681
+ const gatewayAccessTokenExpiresAt = context.auth.accessTokenExpiresAt();
1682
+ const gatewayAccessExpired = !gatewayAccessToken ||
1683
+ isExpired(gatewayAccessTokenExpiresAt, context.refreshSkewMs);
1684
+ if (!forceRefresh && !gatewayAccessExpired && gatewayAccessToken) {
1685
+ return gatewayAccessToken;
1686
+ }
1687
+ const gatewayRefreshToken = context.auth.refreshToken();
1688
+ if (!gatewayRefreshToken ||
1689
+ isExpired(context.auth.refreshTokenExpiresAt())) {
1690
+ throw new GatewaySessionDeadError();
1691
+ }
1692
+ try {
1693
+ await refreshAccessToken({ ...context, scope: 'gateway', applicationCode: undefined }, gatewayRefreshToken);
1694
+ }
1695
+ catch {
1696
+ throw new GatewaySessionDeadError();
1697
+ }
1698
+ const refreshedGatewayAccessToken = context.auth.token();
1699
+ if (!refreshedGatewayAccessToken) {
1700
+ throw new GatewaySessionDeadError();
1701
+ }
1702
+ return refreshedGatewayAccessToken;
1703
+ };
1704
+ const url = buildGatewayUrl(context.gatewayApiBaseUrl, GATEWAY_AUTH_ENDPOINTS.applicationLaunch(code));
1705
+ const params = {};
1706
+ const returnUrl = resolveRelaunchReturnUrl(options, code);
1707
+ if (returnUrl) {
1708
+ params['returnUrl'] = returnUrl;
1709
+ }
1710
+ if (context.deviceToken) {
1711
+ params['deviceToken'] = context.deviceToken;
1712
+ }
1713
+ const requestLaunch = (gatewayAccessToken) => firstValueFrom(context.http
1714
+ .get(url, {
1715
+ params,
1716
+ headers: {
1717
+ Authorization: `Bearer ${gatewayAccessToken}`,
1718
+ },
1719
+ })
1720
+ .pipe(map((response) => response.data), map((data) => {
1721
+ if (!data?.tokens?.accessToken || !data.tokens.refreshToken) {
1722
+ throw new Error('Invalid launch response');
1723
+ }
1724
+ const mapped = mapGatewayTokens(data.tokens);
1725
+ const session = {
1726
+ applicationCode: data.applicationCode,
1727
+ applicationName: data.applicationName,
1728
+ launchUrl: data.launchUrl,
1729
+ accessToken: mapped.accessToken,
1730
+ refreshToken: mapped.refreshToken,
1731
+ accessTokenExpiresAt: mapped.accessTokenExpiresAt,
1732
+ refreshTokenExpiresAt: mapped.refreshTokenExpiresAt,
1733
+ };
1734
+ context.auth.setAppSession(session);
1735
+ return data.tokens;
1736
+ })));
1737
+ const gatewayAccessToken = await ensureGatewayAccessToken();
1738
+ try {
1739
+ return await requestLaunch(gatewayAccessToken);
1740
+ }
1741
+ catch (error) {
1742
+ if (!isUnauthorizedError(error)) {
1743
+ throw error;
1744
+ }
1745
+ const refreshedGatewayAccessToken = await ensureGatewayAccessToken(true);
1746
+ try {
1747
+ return await requestLaunch(refreshedGatewayAccessToken);
1748
+ }
1749
+ catch (retryError) {
1750
+ if (isUnauthorizedError(retryError)) {
1751
+ throw new GatewaySessionDeadError();
1752
+ }
1753
+ throw retryError;
1754
+ }
1755
+ }
1756
+ });
1757
+ };
1758
+ const relaunchApplication = (context, options) => {
1759
+ const code = context.applicationCode;
1760
+ if (!code) {
1761
+ return Promise.reject(new Error('Cannot re-launch without applicationCode'));
1762
+ }
1763
+ const existing = inflightRelaunchByCode.get(code);
1764
+ if (existing) {
1765
+ return existing;
1766
+ }
1767
+ const promise = executeRelaunch(context, options);
1768
+ inflightRelaunchByCode.set(code, promise);
1769
+ promise.finally(() => {
1770
+ if (inflightRelaunchByCode.get(code) === promise) {
1771
+ inflightRelaunchByCode.delete(code);
1772
+ }
1773
+ });
1774
+ return promise;
1775
+ };
1604
1776
  const prepareRequest = (req, token, markRetried, baseUrl) => {
1605
1777
  let modifiedReq = req;
1606
1778
  if (token) {
@@ -1623,13 +1795,46 @@ const prepareRequest = (req, token, markRetried, baseUrl) => {
1623
1795
  return modifiedReq;
1624
1796
  };
1625
1797
  const urlMatchesBase = (url, baseUrl) => {
1626
- if (!baseUrl || !isAbsoluteUrl(url)) {
1798
+ if (!baseUrl) {
1627
1799
  return false;
1628
1800
  }
1629
- return url.startsWith(normalizeBase(baseUrl));
1801
+ const normalizedBase = normalizeBase(baseUrl);
1802
+ if (isAbsoluteUrl(url) && isAbsoluteUrl(normalizedBase)) {
1803
+ return url.startsWith(normalizedBase);
1804
+ }
1805
+ const basePath = getBasePath(baseUrl).replace(/^\/+/, '').replace(/\/+$/, '');
1806
+ if (!basePath) {
1807
+ return !isAbsoluteUrl(url) && url.startsWith(normalizedBase);
1808
+ }
1809
+ let path = url;
1810
+ if (isAbsoluteUrl(url)) {
1811
+ try {
1812
+ path = new URL(url).pathname;
1813
+ }
1814
+ catch {
1815
+ path = url;
1816
+ }
1817
+ }
1818
+ path = path.split('?')[0].replace(/^\/+/, '');
1819
+ return path === basePath || path.startsWith(`${basePath}/`);
1630
1820
  };
1631
- const resolveRequestScope = (req, options, auth, gatewayApiBaseUrl) => {
1632
- // 1. Per-request override wins.
1821
+ const resolveRequestScope = (req, options, auth, gatewayApiBaseUrl, applicationApiBaseUrl) => {
1822
+ // 1. Public app context is deliberately unauthenticated app scope.
1823
+ if (isApplicationContextRequestUrl(req.url, applicationApiBaseUrl)) {
1824
+ return { scope: 'application', useGatewayBaseUrl: false };
1825
+ }
1826
+ // 2. Known Gateway endpoints must always use GatewaySession.
1827
+ if (isGatewayEndpointRequestUrl(req.url, gatewayApiBaseUrl)) {
1828
+ return {
1829
+ scope: 'gateway',
1830
+ useGatewayBaseUrl: !urlMatchesBase(req.url, gatewayApiBaseUrl),
1831
+ };
1832
+ }
1833
+ // 3. Caller hint via request context.
1834
+ if (options.shouldUseGatewayApiBaseUrl?.(req)) {
1835
+ return { scope: 'gateway', useGatewayBaseUrl: true };
1836
+ }
1837
+ // 4. Per-request app override.
1633
1838
  const explicitCode = options.resolveApplicationCodeForRequest?.(req);
1634
1839
  if (explicitCode) {
1635
1840
  const session = auth.getAppSession(explicitCode);
@@ -1640,30 +1845,34 @@ const resolveRequestScope = (req, options, auth, gatewayApiBaseUrl) => {
1640
1845
  useGatewayBaseUrl: false,
1641
1846
  };
1642
1847
  }
1643
- // 2. Caller hint via request context.
1644
- if (options.shouldUseGatewayApiBaseUrl?.(req)) {
1645
- return { scope: 'gateway', useGatewayBaseUrl: true };
1848
+ const defaultCode = resolveApplicationCodeOption(options.applicationCode) ??
1849
+ auth.activeApplicationCode();
1850
+ // 5. Requests targeting the configured app API use ApplicationAccess.
1851
+ if (urlMatchesBase(req.url, applicationApiBaseUrl) && defaultCode) {
1852
+ return {
1853
+ scope: 'application',
1854
+ applicationCode: defaultCode,
1855
+ session: auth.getAppSession(defaultCode),
1856
+ useGatewayBaseUrl: false,
1857
+ };
1646
1858
  }
1647
- // 3. Absolute URL pointing at the Gateway base must use GatewaySession.
1648
- // Covers /api/auth/*, /api/auth/me/applications, /api/applications/{code}/launch.
1859
+ // 6. Absolute URL pointing at the Gateway base must use GatewaySession.
1649
1860
  if (urlMatchesBase(req.url, gatewayApiBaseUrl)) {
1650
1861
  return { scope: 'gateway', useGatewayBaseUrl: false };
1651
1862
  }
1652
- // 4. Otherwise treat as app scope when a session exists.
1653
- const defaultCode = resolveApplicationCodeOption(options.applicationCode) ??
1654
- auth.activeApplicationCode();
1863
+ // 7. App shells with a configured/active app code should not send the
1864
+ // GatewaySession to direct app APIs. The interceptor will launch the app
1865
+ // if the ApplicationAccess pair is not available yet.
1655
1866
  if (defaultCode) {
1656
1867
  const session = auth.getAppSession(defaultCode);
1657
- if (session) {
1658
- return {
1659
- scope: 'application',
1660
- applicationCode: defaultCode,
1661
- session,
1662
- useGatewayBaseUrl: false,
1663
- };
1664
- }
1868
+ return {
1869
+ scope: 'application',
1870
+ applicationCode: defaultCode,
1871
+ session,
1872
+ useGatewayBaseUrl: false,
1873
+ };
1665
1874
  }
1666
- // 5. No app session known yet fall back to GatewaySession.
1875
+ // 8. No app context known yet - fall back to GatewaySession.
1667
1876
  return { scope: 'gateway', useGatewayBaseUrl: false };
1668
1877
  };
1669
1878
  const gatewayAuthInterceptor = (req, next) => {
@@ -1679,21 +1888,18 @@ const gatewayAuthInterceptor = (req, next) => {
1679
1888
  const refreshSkewMs = resolveAccessTokenRefreshSkewMs(options.accessTokenRefreshSkewMs);
1680
1889
  const isAuthRequest = isGatewayAuthRequestUrl(req.url, gatewayApiBaseUrl);
1681
1890
  const alreadyRetried = req.context.get(GATEWAY_AUTH_RETRY_CONTEXT);
1682
- const resolved = resolveRequestScope(req, options, auth, gatewayApiBaseUrl);
1891
+ const resolved = resolveRequestScope(req, options, auth, gatewayApiBaseUrl, appApiBaseUrl);
1683
1892
  const baseUrl = resolved.useGatewayBaseUrl
1684
1893
  ? gatewayApiBaseUrl
1685
1894
  : appApiBaseUrl;
1686
1895
  const gatewayAccessToken = auth.token();
1687
1896
  const session = resolved.session ?? null;
1688
- const accessToken = resolved.scope === 'application' && session
1689
- ? session.accessToken
1897
+ const accessToken = resolved.scope === 'application'
1898
+ ? (session?.accessToken ?? null)
1690
1899
  : gatewayAccessToken;
1691
- // Reactive-only refresh: we never call /auth/refresh proactively. If the
1692
- // access token is genuinely expired the request goes out with the existing
1693
- // one, the backend will 401, and the catchError below refreshes once and
1694
- // retries. This avoids the post-login refresh cascade caused by short
1695
- // access-token TTLs versus any skew.
1696
- const tokenToAttach = !isAuthRequest && accessToken ? accessToken : null;
1900
+ const isApplicationContextRequest = isApplicationContextRequestUrl(req.url, appApiBaseUrl);
1901
+ const shouldAttachAuth = !isAuthRequest && !isApplicationContextRequest;
1902
+ const tokenToAttach = shouldAttachAuth && accessToken ? accessToken : null;
1697
1903
  const buildRefreshContext = (scopeOverride, appCodeOverride) => ({
1698
1904
  http,
1699
1905
  gatewayApiBaseUrl,
@@ -1703,49 +1909,100 @@ const gatewayAuthInterceptor = (req, next) => {
1703
1909
  scope: scopeOverride ?? resolved.scope,
1704
1910
  applicationCode: appCodeOverride ?? resolved.applicationCode,
1705
1911
  });
1706
- const initialAccessToken = accessToken;
1707
- return next(prepareRequest(req, tokenToAttach, false, baseUrl)).pipe(catchError$1((error) => {
1708
- if (error?.status !== 401 || isAuthRequest || alreadyRetried) {
1912
+ const handleRelaunchError = (relaunchError) => {
1913
+ if (isGatewaySessionDeadError(relaunchError)) {
1914
+ auth.logout();
1915
+ }
1916
+ else if (resolved.applicationCode) {
1917
+ auth.clearAppSession(resolved.applicationCode);
1918
+ }
1919
+ return throwError(() => relaunchError);
1920
+ };
1921
+ const relaunch$ = (clearAppSessionBeforeLaunch = true) => {
1922
+ if (clearAppSessionBeforeLaunch && resolved.applicationCode) {
1923
+ auth.clearAppSession(resolved.applicationCode);
1924
+ }
1925
+ return from(relaunchApplication(buildRefreshContext('application', resolved.applicationCode), options)).pipe(catchError$1(handleRelaunchError));
1926
+ };
1927
+ const resolveGatewayTokenBeforeRequest$ = () => {
1928
+ const currentAccessToken = auth.token();
1929
+ const currentAccessTokenExpiresAt = auth.accessTokenExpiresAt();
1930
+ if (currentAccessToken &&
1931
+ !isExpired(currentAccessTokenExpiresAt, refreshSkewMs)) {
1932
+ return of(currentAccessToken);
1933
+ }
1934
+ const gatewayRefreshToken = auth.refreshToken();
1935
+ if (!currentAccessToken && !gatewayRefreshToken) {
1936
+ return of(null);
1937
+ }
1938
+ if (!gatewayRefreshToken || isExpired(auth.refreshTokenExpiresAt())) {
1939
+ auth.logout();
1940
+ return throwError(() => new GatewaySessionDeadError());
1941
+ }
1942
+ return refreshTokens$(buildRefreshContext('gateway', undefined), gatewayRefreshToken).pipe(map((tokens) => tokens.accessToken), catchError$1((refreshError) => {
1943
+ auth.logout();
1944
+ return throwError(() => refreshError);
1945
+ }));
1946
+ };
1947
+ const resolveAppTokenBeforeRequest$ = () => {
1948
+ const code = resolved.applicationCode;
1949
+ if (!code) {
1950
+ return of(tokenToAttach);
1951
+ }
1952
+ const latestSession = auth.getAppSession(code);
1953
+ if (latestSession?.accessToken &&
1954
+ !isExpired(latestSession.accessTokenExpiresAt, refreshSkewMs)) {
1955
+ return of(latestSession.accessToken);
1956
+ }
1957
+ if (!latestSession?.refreshToken ||
1958
+ isExpired(latestSession.refreshTokenExpiresAt)) {
1959
+ return relaunch$().pipe(map((tokens) => tokens.accessToken));
1960
+ }
1961
+ return refreshTokens$(buildRefreshContext('application', code), latestSession.refreshToken).pipe(map((tokens) => tokens.accessToken), catchError$1(() => relaunch$().pipe(map((tokens) => tokens.accessToken))));
1962
+ };
1963
+ const initialToken$ = !shouldAttachAuth
1964
+ ? of(null)
1965
+ : resolved.scope === 'application'
1966
+ ? resolveAppTokenBeforeRequest$()
1967
+ : resolveGatewayTokenBeforeRequest$();
1968
+ return initialToken$.pipe(switchMap$1((requestAccessToken) => next(prepareRequest(req, requestAccessToken, false, baseUrl)).pipe(catchError$1((error) => {
1969
+ if (error?.status !== 401 || !shouldAttachAuth || alreadyRetried) {
1709
1970
  return throwError(() => error);
1710
1971
  }
1711
1972
  // If a concurrent flow already rotated the token while this request was
1712
- // in flight, just retry with the freshest token do NOT trigger another
1973
+ // in flight, just retry with the freshest token - do NOT trigger another
1713
1974
  // refresh against the backend.
1714
1975
  const currentAccessToken = resolved.scope === 'application' && resolved.applicationCode
1715
- ? (auth.getAppSession(resolved.applicationCode)?.accessToken ?? null)
1976
+ ? (auth.getAppSession(resolved.applicationCode)?.accessToken ??
1977
+ null)
1716
1978
  : auth.token();
1717
- if (currentAccessToken && currentAccessToken !== initialAccessToken) {
1979
+ if (currentAccessToken && currentAccessToken !== requestAccessToken) {
1718
1980
  return next(prepareRequest(req, currentAccessToken, true, baseUrl));
1719
1981
  }
1720
- const latestAppSession = resolved.scope === 'application' && resolved.applicationCode
1982
+ const isApp = resolved.scope === 'application' && !!resolved.applicationCode;
1983
+ const latestAppSession = isApp
1721
1984
  ? auth.getAppSession(resolved.applicationCode)
1722
1985
  : null;
1723
- const latestRefreshToken = resolved.scope === 'application' && resolved.applicationCode
1986
+ const latestRefreshToken = isApp
1724
1987
  ? (latestAppSession?.refreshToken ?? null)
1725
1988
  : auth.refreshToken();
1726
- const latestRefreshTokenExpiresAt = resolved.scope === 'application' && resolved.applicationCode
1989
+ const latestRefreshTokenExpiresAt = isApp
1727
1990
  ? (latestAppSession?.refreshTokenExpiresAt ?? null)
1728
1991
  : auth.refreshTokenExpiresAt();
1729
1992
  const canRefreshNow = !!latestRefreshToken && !isExpired(latestRefreshTokenExpiresAt);
1730
- if (!canRefreshNow || !latestRefreshToken) {
1731
- if (resolved.scope === 'application' && resolved.applicationCode) {
1732
- auth.clearAppSession(resolved.applicationCode);
1733
- }
1734
- else {
1735
- auth.logout();
1736
- }
1737
- return throwError(() => error);
1738
- }
1739
- return refreshTokens$(buildRefreshContext(), latestRefreshToken).pipe(catchError$1((refreshError) => {
1740
- if (resolved.scope === 'application' && resolved.applicationCode) {
1741
- auth.clearAppSession(resolved.applicationCode);
1742
- }
1743
- else {
1993
+ const tokens$ = !canRefreshNow || !latestRefreshToken
1994
+ ? isApp
1995
+ ? relaunch$()
1996
+ : (auth.logout(), throwError(() => error))
1997
+ : refreshTokens$(buildRefreshContext(), latestRefreshToken).pipe(catchError$1((refreshError) => {
1998
+ if (isApp) {
1999
+ return relaunch$();
2000
+ }
1744
2001
  auth.logout();
1745
- }
1746
- return throwError(() => refreshError);
1747
- }), switchMap$1((tokens) => next(prepareRequest(req, tokens.accessToken, true, baseUrl))));
1748
- }));
2002
+ return throwError(() => refreshError);
2003
+ }));
2004
+ return tokens$.pipe(switchMap$1((tokens) => next(prepareRequest(req, tokens.accessToken, true, baseUrl))));
2005
+ }))));
1749
2006
  };
1750
2007
 
1751
2008
  const CORRELATION_ID_HEADER = 'X-Correlation-ID';