@opexa/portal-components 0.1.66 → 0.1.70

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,8 +1,6 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
- import { FINGERPRINT_HEADER_KEY } from '../../constants/index.js';
3
2
  import { getSessionQueryKey } from '../../utils/queryKeys.js';
4
3
  import { getSession } from '../services/getSession.js';
5
- import { getFingerprint } from './useSignInMutation.js';
6
4
  export const useSessionQuery = (config) => {
7
5
  return useQuery({
8
6
  gcTime: 1000 * 2,
@@ -10,15 +8,7 @@ export const useSessionQuery = (config) => {
10
8
  ...config,
11
9
  queryKey: getSessionQueryKey(),
12
10
  queryFn: async ({ signal }) => {
13
- const fingerprint = await getFingerprint();
14
- return await getSession({
15
- signal,
16
- headers: {
17
- ...(fingerprint && {
18
- [FINGERPRINT_HEADER_KEY]: fingerprint,
19
- }),
20
- },
21
- });
11
+ return await getSession({ signal });
22
12
  },
23
13
  });
24
14
  };
@@ -8,4 +8,3 @@ export interface UseSignInMutationOptions extends MutationConfig<Authenticator |
8
8
  version?: ApiVersion;
9
9
  }
10
10
  export declare const useSignInMutation: (options?: UseSignInMutationOptions) => UseMutationResult<Authenticator | null, Error, SignInInput>;
11
- export declare function getFingerprint(): Promise<string | null>;
@@ -1,6 +1,5 @@
1
1
  import { Capacitor } from '@capacitor/core';
2
2
  import { useMutation } from '@tanstack/react-query';
3
- import { Thumbmark } from '@thumbmarkjs/thumbmarkjs';
4
3
  import { FINGERPRINT_HEADER_KEY, GOOGLE_FRAUD_DEFENSE_HEADER_KEY, RECAPTCHA_HEADER_KEY, } from '../../constants/index.js';
5
4
  import { createPoll } from '../../utils/createPoll.js';
6
5
  import { getQueryClient } from '../../utils/getQueryClient.js';
@@ -8,6 +7,7 @@ import { getSignInMutationKey } from '../../utils/mutationKeys.js';
8
7
  import { getSessionQueryKey } from '../../utils/queryKeys.js';
9
8
  import { getSession } from '../services/getSession.js';
10
9
  import { signIn } from '../services/signIn.js';
10
+ import { getFingerprint } from '../utils/fingerprint.js';
11
11
  import { useFeatureFlag } from './useFeatureFlag.js';
12
12
  import { useRecaptcha } from './useRecaptcha.js';
13
13
  export const useSignInMutation = (options) => {
@@ -87,26 +87,3 @@ async function pollSignIn(signInOnce) {
87
87
  }
88
88
  return result.authenticator;
89
89
  }
90
- let thumbmark_instance;
91
- export async function getFingerprint() {
92
- if (typeof window === 'undefined')
93
- return null;
94
- if (!thumbmark_instance) {
95
- thumbmark_instance = new Thumbmark({
96
- logging: false,
97
- timeout: 30000,
98
- cache_lifetime_in_ms: 1 * 60 * 60 * 1000 /* 1h */,
99
- });
100
- }
101
- try {
102
- const cached = sessionStorage.getItem('fingerprint');
103
- if (cached)
104
- return cached;
105
- const result = await thumbmark_instance.get();
106
- sessionStorage.setItem('fingerprint', result.thumbmark);
107
- return result.thumbmark;
108
- }
109
- catch {
110
- return null;
111
- }
112
- }
@@ -1,6 +1,16 @@
1
1
  import { cache } from 'react';
2
+ import { FINGERPRINT_HEADER_KEY } from '../../constants/index.js';
2
3
  import { httpRequest, } from '../../services/httpRequest.js';
4
+ import { getFingerprint } from '../utils/fingerprint.js';
3
5
  export const getSession = cache(async (options) => {
4
- const res = await httpRequest.json('/api/sessions', options);
6
+ const fingerprint = await getFingerprint();
7
+ const headers = new Headers(options?.headers);
8
+ if (fingerprint) {
9
+ headers.set(FINGERPRINT_HEADER_KEY, fingerprint);
10
+ }
11
+ const res = await httpRequest.json('/api/sessions', {
12
+ ...options,
13
+ headers,
14
+ });
5
15
  return res.ok ? res.data : { status: 'unauthenticated' };
6
16
  });
@@ -0,0 +1 @@
1
+ export declare function getFingerprint(): Promise<string | null>;
@@ -0,0 +1,24 @@
1
+ import { Thumbmark } from '@thumbmarkjs/thumbmarkjs';
2
+ let thumbmark_instance;
3
+ export async function getFingerprint() {
4
+ if (typeof window === 'undefined')
5
+ return null;
6
+ if (!thumbmark_instance) {
7
+ thumbmark_instance = new Thumbmark({
8
+ logging: false,
9
+ timeout: 30000,
10
+ cache_lifetime_in_ms: 1 * 60 * 60 * 1000 /* 1h */,
11
+ });
12
+ }
13
+ try {
14
+ const cached = sessionStorage.getItem('fingerprint');
15
+ if (cached)
16
+ return cached;
17
+ const result = await thumbmark_instance.get();
18
+ sessionStorage.setItem('fingerprint', result.thumbmark);
19
+ return result.thumbmark;
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
@@ -1,6 +1,6 @@
1
1
  export declare const OnlineBankDepositContext: (props: {
2
2
  value: {
3
- view: "form" | "vca";
3
+ view: "vca" | "form";
4
4
  status: "waiting" | "failed" | "processing" | "verification-waiting" | "verification-processing" | "verification-failed" | "verification-success";
5
5
  verify: () => void;
6
6
  reset: () => void;
@@ -13,7 +13,7 @@ export declare const OnlineBankDepositContext: (props: {
13
13
  } & {
14
14
  children?: import("react").ReactNode | undefined;
15
15
  }) => React.ReactNode, useOnlineBankDepositContext: () => {
16
- view: "form" | "vca";
16
+ view: "vca" | "form";
17
17
  status: "waiting" | "failed" | "processing" | "verification-waiting" | "verification-processing" | "verification-failed" | "verification-success";
18
18
  verify: () => void;
19
19
  reset: () => void;
@@ -1,7 +1,7 @@
1
1
  import type { Deposit } from '../../../../types';
2
2
  export type UseOnlineBankDepositReturn = ReturnType<typeof useOnlineBankDeposit>;
3
3
  export declare function useOnlineBankDeposit(): {
4
- view: "form" | "vca";
4
+ view: "vca" | "form";
5
5
  status: "waiting" | "failed" | "processing" | "verification-waiting" | "verification-processing" | "verification-failed" | "verification-success";
6
6
  verify: () => void;
7
7
  reset: () => void;
@@ -1,5 +1,7 @@
1
1
  'use client';
2
+ import { App } from '@capacitor/app';
2
3
  import { useRouter } from 'next/navigation';
4
+ import { useEffect, useRef } from 'react';
3
5
  import { useTimeout } from 'usehooks-ts';
4
6
  import { useShallow } from 'zustand/shallow';
5
7
  import { useGlobalStore } from '../../client/hooks/useGlobalStore.js';
@@ -7,13 +9,54 @@ import { useSessionHealthQuery } from '../../client/hooks/useSessionHealthQuery.
7
9
  import { useSessionQuery } from '../../client/hooks/useSessionQuery.js';
8
10
  import { useSignOutMutation } from '../../client/hooks/useSignOutMutation.js';
9
11
  import { toaster } from '../../client/utils/toaster.js';
12
+ const SESSION_WATCH_INTERVAL = 30_000;
10
13
  export function SessionWatcher() {
11
14
  const router = useRouter();
12
15
  const sessionQuery = useSessionQuery();
16
+ const isAuthenticated = sessionQuery.data?.status === 'authenticated';
17
+ const isAuthenticatedRef = useRef(isAuthenticated);
18
+ isAuthenticatedRef.current = isAuthenticated;
13
19
  const sessionHealthQuery = useSessionHealthQuery({
14
- enabled: sessionQuery.data?.status === 'authenticated',
15
- refetchInterval: 1000 * 5,
20
+ enabled: false,
16
21
  });
22
+ const refetchSessionHealthRef = useRef(sessionHealthQuery.refetch);
23
+ refetchSessionHealthRef.current = sessionHealthQuery.refetch;
24
+ useEffect(() => {
25
+ let counter = 0;
26
+ let timeout = null;
27
+ let isForeground = true;
28
+ let stopped = false;
29
+ const listener = App.addListener('appStateChange', ({ isActive }) => {
30
+ isForeground = isActive;
31
+ });
32
+ const assignTimeout = () => {
33
+ const duration = counter <= 0 ? SESSION_WATCH_INTERVAL * 1.5 : SESSION_WATCH_INTERVAL;
34
+ return setTimeout(async () => {
35
+ if (isForeground && isAuthenticatedRef.current) {
36
+ try {
37
+ await refetchSessionHealthRef.current();
38
+ }
39
+ catch {
40
+ // A failed check must not stop future session checks.
41
+ }
42
+ if (stopped) {
43
+ return;
44
+ }
45
+ }
46
+ counter += 1;
47
+ timeout = assignTimeout();
48
+ }, duration);
49
+ };
50
+ timeout = assignTimeout();
51
+ return () => {
52
+ stopped = true;
53
+ if (timeout) {
54
+ clearTimeout(timeout);
55
+ timeout = null;
56
+ }
57
+ void listener.then((handle) => handle.remove()).catch(() => undefined);
58
+ };
59
+ }, []);
17
60
  const { gameLaunch, reset } = useGlobalStore(useShallow((ctx) => ({
18
61
  gameLaunch: ctx.gameLaunch,
19
62
  reset: ctx.reset,
@@ -5,8 +5,10 @@ export async function deleteSession(request) {
5
5
  const accessToken = request.cookies.get(ACCESS_TOKEN_COOKIE_NAME)?.value;
6
6
  const authConfig = JSON.parse(request.cookies.get(AUTH_CONFIG_COOKIE_NAME)?.value ?? '{}');
7
7
  const authApiBaseUrl = authConfig.apiBaseUrl;
8
+ console.log('\n');
8
9
  console.log('[DESTROY SESSION]');
9
10
  console.log({ authApiBaseUrl });
11
+ console.log('\n');
10
12
  if (accessToken) {
11
13
  await destroySession({
12
14
  headers: {
@@ -8,12 +8,15 @@ export async function getSession(request) {
8
8
  const authVersion = authConfig.version;
9
9
  const authApiBaseUrl = authConfig.apiBaseUrl;
10
10
  const fingerprint = request.headers.get(FINGERPRINT_HEADER_KEY);
11
+ console.log('\n');
11
12
  console.log('[GET SESSION]');
12
13
  console.log({
13
14
  authVersion,
14
15
  authApiBaseUrl,
15
16
  fingerprint,
17
+ accessToken,
16
18
  });
19
+ console.log('\n');
17
20
  let isAccessTokenValid = true;
18
21
  if (accessToken && authVersion !== 4) {
19
22
  try {
@@ -33,7 +36,18 @@ export async function getSession(request) {
33
36
  const refreshToken = request.cookies.get(REFRESH_TOKEN_COOKIE_NAME)?.value;
34
37
  const domain = request.cookies.get(DOMAIN_COOKIE_NAME)?.value;
35
38
  const btag = request.cookies.get(BTAG_COOKIE_NAME)?.value;
36
- if (!refreshToken || !isAccessTokenValid) {
39
+ console.log('\n');
40
+ console.log({
41
+ refreshToken,
42
+ isAccessTokenValid,
43
+ });
44
+ console.log('\n');
45
+ if (!refreshToken) {
46
+ console.log('\n');
47
+ console.log("No 'refreshToken'");
48
+ console.log('\n');
49
+ }
50
+ if (!refreshToken && !isAccessTokenValid) {
37
51
  const response = NextResponse.json({
38
52
  ok: true,
39
53
  data: {
@@ -53,6 +67,16 @@ export async function getSession(request) {
53
67
  .get('x-forwarded-for')
54
68
  ?.split(',')
55
69
  .at(0);
70
+ console.log('\n');
71
+ console.log('[REFRESH SESSION]');
72
+ console.log('Refreshing session...');
73
+ console.log({
74
+ refreshToken,
75
+ authVersion,
76
+ authApiBaseUrl,
77
+ fingerprint,
78
+ });
79
+ console.log('\n');
56
80
  const data = await refreshSession({
57
81
  headers: {
58
82
  Authorization: `Bearer ${refreshToken}`,
@@ -64,21 +88,26 @@ export async function getSession(request) {
64
88
  }),
65
89
  },
66
90
  }, authVersion, authApiBaseUrl);
91
+ console.log({ data });
67
92
  const response = NextResponse.json({
68
93
  ok: true,
69
94
  data: {
70
95
  status: 'authenticated',
71
96
  domain,
72
- token: data.accessToken,
97
+ token: authVersion === 4
98
+ ? data.data.accessToken
99
+ : data.accessToken,
73
100
  },
74
101
  });
75
- response.cookies.set(ACCESS_TOKEN_COOKIE_NAME, data.accessToken, {
76
- expires: addMinutes(new Date(), 9),
102
+ response.cookies.set(ACCESS_TOKEN_COOKIE_NAME, authVersion === 4 ? data.data.accessToken : data.accessToken, {
103
+ expires: addMinutes(new Date(), authVersion === 4 ? 4 : 9),
77
104
  httpOnly: true,
78
105
  sameSite: 'strict',
79
106
  });
80
107
  if (authVersion === 4) {
81
- response.cookies.set(REFRESH_TOKEN_COOKIE_NAME, data.refreshToken, {
108
+ response.cookies.set(REFRESH_TOKEN_COOKIE_NAME, authVersion === 4
109
+ ? data.data.refreshToken
110
+ : data.refreshToken, {
82
111
  expires: subMinutes(addDays(new Date(), 15), 2),
83
112
  httpOnly: true,
84
113
  sameSite: 'strict',
@@ -86,7 +115,12 @@ export async function getSession(request) {
86
115
  }
87
116
  return response;
88
117
  }
89
- catch {
118
+ catch (e) {
119
+ console.log('\n');
120
+ console.log('[REFRESH SESSION]');
121
+ console.log('Failed to refresh session');
122
+ console.log(e);
123
+ console.log('\n');
90
124
  const response = NextResponse.json({
91
125
  ok: true,
92
126
  data: {
@@ -47,6 +47,7 @@ export async function postSession(request) {
47
47
  const authApiBaseUrl = request.headers.get(AUTH_API_BASE_URL_HEADER_KEY);
48
48
  const version = request.headers.get(API_VERSION_HEADER_KEY) === '4' ? 4 : 1;
49
49
  const isWeb = request.headers.get(PLATFORM_WEB_HEADER_KEY) === 'true';
50
+ console.log('\n');
50
51
  console.log('[CREATE SESSION]');
51
52
  console.log({
52
53
  versionSession,
@@ -55,6 +56,7 @@ export async function postSession(request) {
55
56
  fingerprint,
56
57
  isWeb,
57
58
  });
59
+ console.log('\n');
58
60
  try {
59
61
  const googleFraudDefense = request.headers.get(GOOGLE_FRAUD_DEFENSE_HEADER_KEY);
60
62
  const recaptchaToken = googleFraudDefense ?? recaptcha;
@@ -86,6 +88,10 @@ export async function postSession(request) {
86
88
  });
87
89
  }
88
90
  const response = NextResponse.json({ ok: true, data: null }, { status: 201 });
91
+ console.log('\n');
92
+ console.log('[CREATE SESSION]');
93
+ console.log(res);
94
+ console.log('\n');
89
95
  response.cookies.set(ACCESS_TOKEN_COOKIE_NAME, res.accessToken, {
90
96
  expires: addMinutes(new Date(), version === 4 ? 4 : 9),
91
97
  httpOnly: true,
@@ -143,16 +143,26 @@ export async function authenticate(input, options) {
143
143
  throw new Error("Invalid input 'type'");
144
144
  }
145
145
  export async function refreshSession(options, version = 1, authApiBaseUrl) {
146
- const url = authApiBaseUrl || AUTH_ENDPOINT;
147
- try {
148
- return await httpRequest.json(version !== 1
146
+ let url = '';
147
+ url = authApiBaseUrl || AUTH_ENDPOINT;
148
+ url =
149
+ version !== 1
149
150
  ? `${url}/v${version}/session/refresh`
150
- : `${url}/session:refresh`, {
151
+ : `${url}/session:refresh`;
152
+ console.log('\n');
153
+ console.log('[REFRESH SESSION]');
154
+ console.log({ url });
155
+ console.log('\n');
156
+ try {
157
+ return await httpRequest.json(url, {
151
158
  ...options,
152
159
  method: 'POST',
153
160
  });
154
161
  }
155
162
  catch (e) {
163
+ console.log('[REFRESH SESSION]');
164
+ console.log('Failed to refresh session');
165
+ console.log(e);
156
166
  if (e instanceof Error) {
157
167
  throw e;
158
168
  }
@@ -166,9 +176,15 @@ export async function refreshSession(options, version = 1, authApiBaseUrl) {
166
176
  }
167
177
  }
168
178
  export async function destroySession(options, authApiBaseUrl) {
169
- const url = authApiBaseUrl || AUTH_ENDPOINT;
179
+ let url = '';
180
+ url = authApiBaseUrl || AUTH_ENDPOINT;
181
+ url = `${url}/session`;
182
+ console.log('\n');
183
+ console.log('[DESTROY SESSION]');
184
+ console.log({ url });
185
+ console.log('\n');
170
186
  try {
171
- await httpRequest(`${url}/session`, {
187
+ await httpRequest(url, {
172
188
  ...options,
173
189
  method: 'DELETE',
174
190
  });
@@ -180,9 +196,18 @@ export async function destroySession(options, authApiBaseUrl) {
180
196
  export async function getSessionHealth(options) {
181
197
  try {
182
198
  const res = await fetch(`${AUTH_ENDPOINT}/session`, options);
199
+ if (!res.ok) {
200
+ console.log('\n');
201
+ console.log('[SESSION]');
202
+ console.log("'/session' failed");
203
+ console.log('\n');
204
+ }
183
205
  return res.ok;
184
206
  }
185
207
  catch {
208
+ console.log('\n');
209
+ console.log("unknown error in '/session'");
210
+ console.log('\n');
186
211
  /* Network errors should not be logged out */
187
212
  return true;
188
213
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opexa/portal-components",
3
- "version": "0.1.66",
3
+ "version": "0.1.70",
4
4
  "exports": {
5
5
  "./ui/*": {
6
6
  "types": "./dist/ui/*/index.d.ts",