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

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
@@ -1398,18 +1398,24 @@ const resolveExpiresAt = (data) => {
1398
1398
  return Date.now() + data.expires_in * 1000;
1399
1399
  return null;
1400
1400
  };
1401
+ const resolveEndpoint = (template, realm) => {
1402
+ if (!realm)
1403
+ return template;
1404
+ return template.replace("{realm}", realm).replace(":realm", realm);
1405
+ };
1401
1406
  const createAuthProvider = (options) => {
1402
- var _a, _b, _c, _d, _e, _f, _g, _h;
1407
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1403
1408
  const storageKey = (_a = options.storageKey) !== null && _a !== void 0 ? _a : "neog_auth";
1404
1409
  const authBaseUrl = (_b = options.authBaseUrl) !== null && _b !== void 0 ? _b : options.baseUrl;
1405
1410
  const authRequestFormat = (_c = options.authRequestFormat) !== null && _c !== void 0 ? _c : "form";
1406
1411
  const authGrantType = (_d = options.authGrantType) !== null && _d !== void 0 ? _d : "password";
1407
1412
  const authScope = (_e = options.authScope) !== null && _e !== void 0 ? _e : "openid profile email";
1413
+ const userInfoEndpoint = (_f = options.userInfoEndpoint) !== null && _f !== void 0 ? _f : "/realms/{realm}/protocol/openid-connect/userinfo";
1408
1414
  const stored = loadAuthState(storageKey);
1409
1415
  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,
1416
+ token: (_g = stored === null || stored === void 0 ? void 0 : stored.accessToken) !== null && _g !== void 0 ? _g : null,
1417
+ refreshToken: (_h = stored === null || stored === void 0 ? void 0 : stored.refreshToken) !== null && _h !== void 0 ? _h : null,
1418
+ user: (_j = stored === null || stored === void 0 ? void 0 : stored.user) !== null && _j !== void 0 ? _j : null,
1413
1419
  isAuthenticated: Boolean(stored === null || stored === void 0 ? void 0 : stored.accessToken),
1414
1420
  };
1415
1421
  const AuthContext = require$$0.createContext(undefined);
@@ -1427,6 +1433,7 @@ const createAuthProvider = (options) => {
1427
1433
  saveAuthState(storageKey, payload);
1428
1434
  };
1429
1435
  const getToken = () => authRef.current.token;
1436
+ const getUser = () => { var _a; return (_a = authRef.current.user) !== null && _a !== void 0 ? _a : null; };
1430
1437
  const setToken = (token, refreshToken, user) => {
1431
1438
  var _a, _b, _c, _d, _e;
1432
1439
  authRef.current = {
@@ -1444,6 +1451,24 @@ const createAuthProvider = (options) => {
1444
1451
  clearAuthState(storageKey);
1445
1452
  (_a = stateSetterRef.current) === null || _a === void 0 ? void 0 : _a.call(stateSetterRef, authRef.current);
1446
1453
  };
1454
+ const fetchUserInfo = async (accessToken) => {
1455
+ var _a, _b;
1456
+ const endpoint = resolveEndpoint(userInfoEndpoint, options.authRealm);
1457
+ const url = `${authBaseUrl}${endpoint}`;
1458
+ try {
1459
+ const response = await axios.get(url, {
1460
+ headers: { Authorization: `Bearer ${accessToken}` },
1461
+ timeout: options.requestTimeoutMs,
1462
+ });
1463
+ return response.data;
1464
+ }
1465
+ catch (err) {
1466
+ const message = getErrorMessage(err, "Falha ao carregar userinfo");
1467
+ const status = axios.isAxiosError(err) ? (_a = err.response) === null || _a === void 0 ? void 0 : _a.status : undefined;
1468
+ (_b = options.onErrorLog) === null || _b === void 0 ? void 0 : _b.call(options, { url, status, message });
1469
+ return null;
1470
+ }
1471
+ };
1447
1472
  const refreshAccessToken = async () => {
1448
1473
  if (!options.tokenRefreshEndpoint)
1449
1474
  return null;
@@ -1565,6 +1590,27 @@ const createAuthProvider = (options) => {
1565
1590
  }
1566
1591
  void handleRefreshIfExpired();
1567
1592
  }, [handleRefreshIfExpired]);
1593
+ require$$0.useEffect(() => {
1594
+ if (!state.token || state.user)
1595
+ return;
1596
+ let cancelled = false;
1597
+ void (async () => {
1598
+ var _a, _b;
1599
+ const info = await fetchUserInfo(state.token);
1600
+ if (!info || cancelled)
1601
+ return;
1602
+ const nextState = {
1603
+ token: authRef.current.token,
1604
+ refreshToken: authRef.current.refreshToken,
1605
+ user: info,
1606
+ isAuthenticated: Boolean(authRef.current.token),
1607
+ };
1608
+ syncState(nextState, (_b = (_a = loadAuthState(storageKey)) === null || _a === void 0 ? void 0 : _a.expiresAt) !== null && _b !== void 0 ? _b : null);
1609
+ })();
1610
+ return () => {
1611
+ cancelled = true;
1612
+ };
1613
+ }, [state.token, state.user, syncState]);
1568
1614
  const login = require$$0.useCallback(async (username, password) => {
1569
1615
  var _a, _b, _c;
1570
1616
  setLoading(true);
@@ -1600,7 +1646,8 @@ const createAuthProvider = (options) => {
1600
1646
  const token = data.access_token;
1601
1647
  const refreshToken = (_b = data.refresh_token) !== null && _b !== void 0 ? _b : null;
1602
1648
  const expiresAt = resolveExpiresAt(data);
1603
- const user = (_c = data.user) !== null && _c !== void 0 ? _c : null;
1649
+ const userInfo = token ? await fetchUserInfo(token) : null;
1650
+ const user = (_c = userInfo !== null && userInfo !== void 0 ? userInfo : data.user) !== null && _c !== void 0 ? _c : null;
1604
1651
  const nextState = {
1605
1652
  token,
1606
1653
  refreshToken,
@@ -1642,12 +1689,12 @@ const createAuthProvider = (options) => {
1642
1689
  login,
1643
1690
  logout: handleLogout,
1644
1691
  getToken: () => authRef.current.token,
1645
- setToken: (token, refreshToken) => {
1692
+ setToken: (token, refreshToken, user) => {
1646
1693
  var _a, _b, _c;
1647
1694
  const nextState = {
1648
1695
  token,
1649
1696
  refreshToken: refreshToken !== null && refreshToken !== void 0 ? refreshToken : null,
1650
- user: (_a = authRef.current.user) !== null && _a !== void 0 ? _a : null,
1697
+ user: (_a = user !== null && user !== void 0 ? user : authRef.current.user) !== null && _a !== void 0 ? _a : null,
1651
1698
  isAuthenticated: Boolean(token),
1652
1699
  };
1653
1700
  syncState(nextState, (_c = (_b = loadAuthState(storageKey)) === null || _b === void 0 ? void 0 : _b.expiresAt) !== null && _c !== void 0 ? _c : null);
@@ -1667,6 +1714,7 @@ const createAuthProvider = (options) => {
1667
1714
  AuthProvider,
1668
1715
  useAuth,
1669
1716
  getToken,
1717
+ getUser,
1670
1718
  setToken,
1671
1719
  logout,
1672
1720
  refreshAccessToken,
@@ -1689,7 +1737,7 @@ const shouldRetry = (config) => {
1689
1737
  const createNeogClient = (deps, opts) => {
1690
1738
  var _a;
1691
1739
  const instance = axios.create({
1692
- baseURL: deps.baseUrl,
1740
+ baseURL: deps.baseUrlResolver ? deps.baseUrlResolver() : deps.baseUrl,
1693
1741
  timeout: (_a = opts === null || opts === void 0 ? void 0 : opts.timeoutMs) !== null && _a !== void 0 ? _a : deps.requestTimeoutMs,
1694
1742
  headers: {
1695
1743
  ...deps.defaultHeaders,
@@ -1697,13 +1745,16 @@ const createNeogClient = (deps, opts) => {
1697
1745
  },
1698
1746
  });
1699
1747
  instance.interceptors.request.use((config) => {
1700
- var _a, _b;
1748
+ var _a, _b, _c;
1749
+ if (deps.baseUrlResolver) {
1750
+ config.baseURL = (_a = deps.baseUrlResolver()) !== null && _a !== void 0 ? _a : deps.baseUrl;
1751
+ }
1701
1752
  if (config.url && config.method) {
1702
- (_a = deps.onRequestLog) === null || _a === void 0 ? void 0 : _a.call(deps, { url: config.url, method: config.method });
1753
+ (_b = deps.onRequestLog) === null || _b === void 0 ? void 0 : _b.call(deps, { url: config.url, method: config.method });
1703
1754
  }
1704
1755
  const token = deps.getToken();
1705
1756
  if (token) {
1706
- const headers = axios.AxiosHeaders.from(((_b = config.headers) !== null && _b !== void 0 ? _b : {}));
1757
+ const headers = axios.AxiosHeaders.from(((_c = config.headers) !== null && _c !== void 0 ? _c : {}));
1707
1758
  headers.set("Authorization", `Bearer ${token}`);
1708
1759
  config.headers = headers;
1709
1760
  }
@@ -1764,10 +1815,41 @@ const createNeogClient = (deps, opts) => {
1764
1815
  };
1765
1816
  };
1766
1817
 
1818
+ const getHostname = () => {
1819
+ if (typeof window === "undefined")
1820
+ return null;
1821
+ return window.location.hostname;
1822
+ };
1823
+ const getApiUrl = (user, defaultUrl) => {
1824
+ try {
1825
+ if (!(user === null || user === void 0 ? void 0 : user.neog_api_url)) {
1826
+ return defaultUrl;
1827
+ }
1828
+ const currentHostname = getHostname();
1829
+ if (currentHostname && Array.isArray(user.neog_api_url.location)) {
1830
+ const matchingLocation = user.neog_api_url.location.find((location) => location.host === currentHostname);
1831
+ if (matchingLocation === null || matchingLocation === void 0 ? void 0 : matchingLocation.api_url) {
1832
+ return matchingLocation.api_url;
1833
+ }
1834
+ }
1835
+ if (user.neog_api_url.url) {
1836
+ return user.neog_api_url.url;
1837
+ }
1838
+ return defaultUrl;
1839
+ }
1840
+ catch (error) {
1841
+ if (typeof console !== "undefined") {
1842
+ console.warn("Error determining API URL, using default:", error);
1843
+ }
1844
+ return defaultUrl;
1845
+ }
1846
+ };
1847
+
1767
1848
  function createNeogAuth(options) {
1768
- const { AuthProvider, useAuth, getToken, logout, refreshAccessToken } = createAuthProvider(options);
1849
+ const { AuthProvider, useAuth, getToken, getUser, logout, refreshAccessToken } = createAuthProvider(options);
1769
1850
  const createNeogClient$1 = (opts) => createNeogClient({
1770
1851
  baseUrl: options.baseUrl,
1852
+ baseUrlResolver: () => getApiUrl(getUser(), options.baseUrl),
1771
1853
  getToken,
1772
1854
  refreshToken: refreshAccessToken,
1773
1855
  logout,