@neog-cloud/neog-api-client 0.1.2 → 0.1.4

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,5 +1,5 @@
1
1
  import React from "react";
2
- import { AuthContextType, AuthState } from "./types";
2
+ import { AuthContextType, AuthState, UserInfo } from "./types";
3
3
  import { NeogAuthOptions } from "./options";
4
4
  export type AuthProviderFactory = {
5
5
  AuthProvider: React.FC<{
@@ -7,6 +7,7 @@ export type AuthProviderFactory = {
7
7
  }>;
8
8
  useAuth: () => AuthContextType;
9
9
  getToken: () => string | null;
10
+ getUser: () => UserInfo;
10
11
  setToken: (token: string | null, refreshToken?: string | null, user?: AuthState["user"]) => void;
11
12
  logout: () => void;
12
13
  refreshAccessToken: () => Promise<string | null>;
@@ -2,6 +2,7 @@ export type NeogAuthOptions = {
2
2
  baseUrl: string;
3
3
  authBaseUrl?: string;
4
4
  authRealm?: string;
5
+ userInfoEndpoint?: string;
5
6
  authEndpoint: string;
6
7
  authRequestFormat?: "json" | "form";
7
8
  authGrantType?: string;
@@ -1,12 +1,9 @@
1
+ import { UserInfo } from "./types";
1
2
  export interface StoredAuthState {
2
3
  accessToken: string | null;
3
4
  refreshToken?: string | null;
4
5
  expiresAt?: number | null;
5
- user?: {
6
- id: string;
7
- name?: string;
8
- email?: string;
9
- } | null;
6
+ user?: UserInfo;
10
7
  }
11
8
  export declare const saveAuthState: (storageKey: string, state: StoredAuthState) => void;
12
9
  export declare const loadAuthState: (storageKey: string) => StoredAuthState | null;
@@ -1,27 +1,40 @@
1
+ export type ApiLocation = {
2
+ host: string;
3
+ api_url: string;
4
+ };
5
+ export type NeogApiUrl = {
6
+ location?: ApiLocation[];
7
+ url?: string;
8
+ };
9
+ export type UserInfo = {
10
+ id?: string;
11
+ sub?: string;
12
+ name?: string;
13
+ email?: string;
14
+ preferred_username?: string;
15
+ given_name?: string;
16
+ family_name?: string;
17
+ comp_refid?: string;
18
+ permissions?: unknown;
19
+ neog_api_url?: NeogApiUrl;
20
+ [key: string]: unknown;
21
+ } | null;
1
22
  export interface AuthContextType {
2
23
  token: string | null;
3
24
  refreshToken?: string | null;
4
- user?: {
5
- id: string;
6
- name?: string;
7
- email?: string;
8
- } | null;
25
+ user?: UserInfo;
9
26
  isAuthenticated: boolean;
10
27
  loading: boolean;
11
28
  error?: string | null;
12
29
  login: (username: string, password: string) => Promise<void>;
13
30
  logout: () => void;
14
31
  getToken: () => string | null;
15
- setToken?: (token: string, refreshToken?: string) => void;
32
+ setToken?: (token: string | null, refreshToken?: string | null, user?: UserInfo) => void;
16
33
  }
17
34
  export type AuthState = {
18
35
  token: string | null;
19
36
  refreshToken?: string | null;
20
- user?: {
21
- id: string;
22
- name?: string;
23
- email?: string;
24
- } | null;
37
+ user?: UserInfo;
25
38
  isAuthenticated: boolean;
26
39
  };
27
40
  export type TokenResponse = {
@@ -29,9 +42,5 @@ export type TokenResponse = {
29
42
  refresh_token?: string;
30
43
  expires_in?: number;
31
44
  expires_at?: number;
32
- user?: {
33
- id: string;
34
- name?: string;
35
- email?: string;
36
- } | null;
45
+ user?: UserInfo;
37
46
  };
@@ -5,6 +5,7 @@ export type GetTokenHandler = () => string | null;
5
5
  export type LogoutHandler = () => void;
6
6
  export type CreateClientDeps = {
7
7
  baseUrl: string;
8
+ baseUrlResolver?: () => string;
8
9
  getToken: GetTokenHandler;
9
10
  refreshToken: RefreshTokenHandler;
10
11
  logout: LogoutHandler;
package/dist/index.cjs CHANGED
@@ -1391,6 +1391,8 @@ const getErrorMessage = (error, fallback = "Erro ao processar a requisição") =
1391
1391
  return fallback;
1392
1392
  };
1393
1393
 
1394
+ const USERINFO_ERROR_MESSAGE = "Nao foi possivel carregar dados do usuario";
1395
+ const USERINFO_RETRY_DELAY_MS = 500;
1394
1396
  const resolveExpiresAt = (data) => {
1395
1397
  if (data.expires_at)
1396
1398
  return data.expires_at;
@@ -1398,18 +1400,24 @@ const resolveExpiresAt = (data) => {
1398
1400
  return Date.now() + data.expires_in * 1000;
1399
1401
  return null;
1400
1402
  };
1403
+ const resolveEndpoint = (template, realm) => {
1404
+ if (!realm)
1405
+ return template;
1406
+ return template.replace("{realm}", realm).replace(":realm", realm);
1407
+ };
1401
1408
  const createAuthProvider = (options) => {
1402
- var _a, _b, _c, _d, _e, _f, _g, _h;
1409
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1403
1410
  const storageKey = (_a = options.storageKey) !== null && _a !== void 0 ? _a : "neog_auth";
1404
1411
  const authBaseUrl = (_b = options.authBaseUrl) !== null && _b !== void 0 ? _b : options.baseUrl;
1405
1412
  const authRequestFormat = (_c = options.authRequestFormat) !== null && _c !== void 0 ? _c : "form";
1406
1413
  const authGrantType = (_d = options.authGrantType) !== null && _d !== void 0 ? _d : "password";
1407
1414
  const authScope = (_e = options.authScope) !== null && _e !== void 0 ? _e : "openid profile email";
1415
+ const userInfoEndpoint = (_f = options.userInfoEndpoint) !== null && _f !== void 0 ? _f : "/realms/{realm}/protocol/openid-connect/userinfo";
1408
1416
  const stored = loadAuthState(storageKey);
1409
1417
  const initialState = {
1410
- token: (_f = stored === null || stored === void 0 ? void 0 : stored.accessToken) !== null && _f !== void 0 ? _f : null,
1411
- refreshToken: (_g = stored === null || stored === void 0 ? void 0 : stored.refreshToken) !== null && _g !== void 0 ? _g : null,
1412
- user: (_h = stored === null || stored === void 0 ? void 0 : stored.user) !== null && _h !== void 0 ? _h : null,
1418
+ token: (_g = stored === null || stored === void 0 ? void 0 : stored.accessToken) !== null && _g !== void 0 ? _g : null,
1419
+ refreshToken: (_h = stored === null || stored === void 0 ? void 0 : stored.refreshToken) !== null && _h !== void 0 ? _h : null,
1420
+ user: (_j = stored === null || stored === void 0 ? void 0 : stored.user) !== null && _j !== void 0 ? _j : null,
1413
1421
  isAuthenticated: Boolean(stored === null || stored === void 0 ? void 0 : stored.accessToken),
1414
1422
  };
1415
1423
  const AuthContext = require$$0.createContext(undefined);
@@ -1427,6 +1435,7 @@ const createAuthProvider = (options) => {
1427
1435
  saveAuthState(storageKey, payload);
1428
1436
  };
1429
1437
  const getToken = () => authRef.current.token;
1438
+ const getUser = () => { var _a; return (_a = authRef.current.user) !== null && _a !== void 0 ? _a : null; };
1430
1439
  const setToken = (token, refreshToken, user) => {
1431
1440
  var _a, _b, _c, _d, _e;
1432
1441
  authRef.current = {
@@ -1444,6 +1453,32 @@ const createAuthProvider = (options) => {
1444
1453
  clearAuthState(storageKey);
1445
1454
  (_a = stateSetterRef.current) === null || _a === void 0 ? void 0 : _a.call(stateSetterRef, authRef.current);
1446
1455
  };
1456
+ const fetchUserInfo = async (accessToken) => {
1457
+ var _a, _b;
1458
+ const endpoint = resolveEndpoint(userInfoEndpoint, options.authRealm);
1459
+ const url = `${authBaseUrl}${endpoint}`;
1460
+ try {
1461
+ const response = await axios.get(url, {
1462
+ headers: { Authorization: `Bearer ${accessToken}` },
1463
+ timeout: options.requestTimeoutMs,
1464
+ });
1465
+ return response.data;
1466
+ }
1467
+ catch (err) {
1468
+ const message = getErrorMessage(err, "Falha ao carregar userinfo");
1469
+ const status = axios.isAxiosError(err) ? (_a = err.response) === null || _a === void 0 ? void 0 : _a.status : undefined;
1470
+ (_b = options.onErrorLog) === null || _b === void 0 ? void 0 : _b.call(options, { url, status, message });
1471
+ return null;
1472
+ }
1473
+ };
1474
+ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
1475
+ const fetchUserInfoWithRetry = async (accessToken) => {
1476
+ const info = await fetchUserInfo(accessToken);
1477
+ if (info)
1478
+ return info;
1479
+ await wait(USERINFO_RETRY_DELAY_MS);
1480
+ return fetchUserInfo(accessToken);
1481
+ };
1447
1482
  const refreshAccessToken = async () => {
1448
1483
  if (!options.tokenRefreshEndpoint)
1449
1484
  return null;
@@ -1565,8 +1600,36 @@ const createAuthProvider = (options) => {
1565
1600
  }
1566
1601
  void handleRefreshIfExpired();
1567
1602
  }, [handleRefreshIfExpired]);
1603
+ require$$0.useEffect(() => {
1604
+ if (!state.token || state.user)
1605
+ return;
1606
+ let cancelled = false;
1607
+ void (async () => {
1608
+ var _a, _b;
1609
+ const info = await fetchUserInfoWithRetry(state.token);
1610
+ if (cancelled)
1611
+ return;
1612
+ if (!info) {
1613
+ handleLogout();
1614
+ if (isMounted.current) {
1615
+ setError(USERINFO_ERROR_MESSAGE);
1616
+ }
1617
+ return;
1618
+ }
1619
+ const nextState = {
1620
+ token: authRef.current.token,
1621
+ refreshToken: authRef.current.refreshToken,
1622
+ user: info,
1623
+ isAuthenticated: Boolean(authRef.current.token),
1624
+ };
1625
+ syncState(nextState, (_b = (_a = loadAuthState(storageKey)) === null || _a === void 0 ? void 0 : _a.expiresAt) !== null && _b !== void 0 ? _b : null);
1626
+ })();
1627
+ return () => {
1628
+ cancelled = true;
1629
+ };
1630
+ }, [state.token, state.user, syncState, handleLogout]);
1568
1631
  const login = require$$0.useCallback(async (username, password) => {
1569
- var _a, _b, _c;
1632
+ var _a, _b;
1570
1633
  setLoading(true);
1571
1634
  setError(null);
1572
1635
  try {
@@ -1600,7 +1663,13 @@ const createAuthProvider = (options) => {
1600
1663
  const token = data.access_token;
1601
1664
  const refreshToken = (_b = data.refresh_token) !== null && _b !== void 0 ? _b : null;
1602
1665
  const expiresAt = resolveExpiresAt(data);
1603
- const user = (_c = data.user) !== null && _c !== void 0 ? _c : null;
1666
+ const userInfo = token ? await fetchUserInfoWithRetry(token) : null;
1667
+ if (token && !userInfo) {
1668
+ handleLogout();
1669
+ setError(USERINFO_ERROR_MESSAGE);
1670
+ throw new Error(USERINFO_ERROR_MESSAGE);
1671
+ }
1672
+ const user = userInfo !== null && userInfo !== void 0 ? userInfo : null;
1604
1673
  const nextState = {
1605
1674
  token,
1606
1675
  refreshToken,
@@ -1629,6 +1698,7 @@ const createAuthProvider = (options) => {
1629
1698
  authRequestFormat,
1630
1699
  authGrantType,
1631
1700
  authScope,
1701
+ handleLogout,
1632
1702
  ]);
1633
1703
  const contextValue = require$$0.useMemo(() => {
1634
1704
  var _a, _b;
@@ -1642,12 +1712,12 @@ const createAuthProvider = (options) => {
1642
1712
  login,
1643
1713
  logout: handleLogout,
1644
1714
  getToken: () => authRef.current.token,
1645
- setToken: (token, refreshToken) => {
1715
+ setToken: (token, refreshToken, user) => {
1646
1716
  var _a, _b, _c;
1647
1717
  const nextState = {
1648
1718
  token,
1649
1719
  refreshToken: refreshToken !== null && refreshToken !== void 0 ? refreshToken : null,
1650
- user: (_a = authRef.current.user) !== null && _a !== void 0 ? _a : null,
1720
+ user: (_a = user !== null && user !== void 0 ? user : authRef.current.user) !== null && _a !== void 0 ? _a : null,
1651
1721
  isAuthenticated: Boolean(token),
1652
1722
  };
1653
1723
  syncState(nextState, (_c = (_b = loadAuthState(storageKey)) === null || _b === void 0 ? void 0 : _b.expiresAt) !== null && _c !== void 0 ? _c : null);
@@ -1667,6 +1737,7 @@ const createAuthProvider = (options) => {
1667
1737
  AuthProvider,
1668
1738
  useAuth,
1669
1739
  getToken,
1740
+ getUser,
1670
1741
  setToken,
1671
1742
  logout,
1672
1743
  refreshAccessToken,
@@ -1689,7 +1760,7 @@ const shouldRetry = (config) => {
1689
1760
  const createNeogClient = (deps, opts) => {
1690
1761
  var _a;
1691
1762
  const instance = axios.create({
1692
- baseURL: deps.baseUrl,
1763
+ baseURL: deps.baseUrlResolver ? deps.baseUrlResolver() : deps.baseUrl,
1693
1764
  timeout: (_a = opts === null || opts === void 0 ? void 0 : opts.timeoutMs) !== null && _a !== void 0 ? _a : deps.requestTimeoutMs,
1694
1765
  headers: {
1695
1766
  ...deps.defaultHeaders,
@@ -1697,13 +1768,16 @@ const createNeogClient = (deps, opts) => {
1697
1768
  },
1698
1769
  });
1699
1770
  instance.interceptors.request.use((config) => {
1700
- var _a, _b;
1771
+ var _a, _b, _c;
1772
+ if (deps.baseUrlResolver) {
1773
+ config.baseURL = (_a = deps.baseUrlResolver()) !== null && _a !== void 0 ? _a : deps.baseUrl;
1774
+ }
1701
1775
  if (config.url && config.method) {
1702
- (_a = deps.onRequestLog) === null || _a === void 0 ? void 0 : _a.call(deps, { url: config.url, method: config.method });
1776
+ (_b = deps.onRequestLog) === null || _b === void 0 ? void 0 : _b.call(deps, { url: config.url, method: config.method });
1703
1777
  }
1704
1778
  const token = deps.getToken();
1705
1779
  if (token) {
1706
- const headers = axios.AxiosHeaders.from(((_b = config.headers) !== null && _b !== void 0 ? _b : {}));
1780
+ const headers = axios.AxiosHeaders.from(((_c = config.headers) !== null && _c !== void 0 ? _c : {}));
1707
1781
  headers.set("Authorization", `Bearer ${token}`);
1708
1782
  config.headers = headers;
1709
1783
  }
@@ -1764,10 +1838,41 @@ const createNeogClient = (deps, opts) => {
1764
1838
  };
1765
1839
  };
1766
1840
 
1841
+ const getHostname = () => {
1842
+ if (typeof window === "undefined")
1843
+ return null;
1844
+ return window.location.hostname;
1845
+ };
1846
+ const getApiUrl = (user, defaultUrl) => {
1847
+ try {
1848
+ if (!(user === null || user === void 0 ? void 0 : user.neog_api_url)) {
1849
+ return defaultUrl;
1850
+ }
1851
+ const currentHostname = getHostname();
1852
+ if (currentHostname && Array.isArray(user.neog_api_url.location)) {
1853
+ const matchingLocation = user.neog_api_url.location.find((location) => location.host === currentHostname);
1854
+ if (matchingLocation === null || matchingLocation === void 0 ? void 0 : matchingLocation.api_url) {
1855
+ return matchingLocation.api_url;
1856
+ }
1857
+ }
1858
+ if (user.neog_api_url.url) {
1859
+ return user.neog_api_url.url;
1860
+ }
1861
+ return defaultUrl;
1862
+ }
1863
+ catch (error) {
1864
+ if (typeof console !== "undefined") {
1865
+ console.warn("Error determining API URL, using default:", error);
1866
+ }
1867
+ return defaultUrl;
1868
+ }
1869
+ };
1870
+
1767
1871
  function createNeogAuth(options) {
1768
- const { AuthProvider, useAuth, getToken, logout, refreshAccessToken } = createAuthProvider(options);
1872
+ const { AuthProvider, useAuth, getToken, getUser, logout, refreshAccessToken } = createAuthProvider(options);
1769
1873
  const createNeogClient$1 = (opts) => createNeogClient({
1770
1874
  baseUrl: options.baseUrl,
1875
+ baseUrlResolver: () => getApiUrl(getUser(), options.baseUrl),
1771
1876
  getToken,
1772
1877
  refreshToken: refreshAccessToken,
1773
1878
  logout,