win-portal-auth-sdk-react 1.0.0
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.
- package/README.md +49 -0
- package/dist/index.d.mts +98 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +237 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +207 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# win-portal-auth-sdk-react
|
|
2
|
+
|
|
3
|
+
React bindings for Win Portal Auth SDK — session management with Socket.IO. Headless (no UI); your app implements dialogs.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add win-portal-auth-sdk-react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies: `react`, `react-dom`, `socket.io-client` (optional).
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Wrap your app (or protected layout) with `SessionProvider`, then use `useSession()` to drive your own dialogs.
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { SessionProvider, useSession } from 'win-portal-auth-sdk-react';
|
|
19
|
+
|
|
20
|
+
<SessionProvider
|
|
21
|
+
token={token}
|
|
22
|
+
wsUrl="https://portal.example.com"
|
|
23
|
+
userId={currentUser?.id}
|
|
24
|
+
tokenStorageKey="my-app-auth-token"
|
|
25
|
+
onTokenRefresh={async () => {
|
|
26
|
+
const res = await authService.refreshToken();
|
|
27
|
+
return res.data.token;
|
|
28
|
+
}}
|
|
29
|
+
onSessionEnd={() => { window.location.href = '/login'; }}
|
|
30
|
+
>
|
|
31
|
+
<App />
|
|
32
|
+
<SessionDialogs />
|
|
33
|
+
</SessionProvider>
|
|
34
|
+
|
|
35
|
+
function SessionDialogs() {
|
|
36
|
+
const { sessionState, remainingSeconds, continueSession } = useSession();
|
|
37
|
+
if (sessionState === 'expiring') return <YourWarningDialog onContinue={continueSession} seconds={remainingSeconds} />;
|
|
38
|
+
if (sessionState === 'expired' || sessionState === 'revoked') return <YourExpiredDialog />;
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## API
|
|
44
|
+
|
|
45
|
+
- **SessionProvider** — props: `token`, `wsUrl`, `userId?`, `tokenStorageKey?`, `onTokenRefresh?`, `onSessionEnd?`
|
|
46
|
+
- **useSession()** — returns `isConnected`, `sessionState`, `remainingSeconds`, `expiringData`, `expiredData`, `revokedData`, `continueSession`, `sendHeartbeat`
|
|
47
|
+
- **useSessionEvents(options)** — lower-level hook (token, wsUrl, callbacks) when you don’t use the provider
|
|
48
|
+
|
|
49
|
+
See [app-integration guide](../auth-sdk/docs/guides/app-integration.md) in auth-sdk for full integration details.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Session state machine: idle (no token/not connected) -> active -> expiring -> expired | revoked
|
|
7
|
+
*/
|
|
8
|
+
type SessionState = 'idle' | 'active' | 'expiring' | 'expired' | 'revoked';
|
|
9
|
+
interface SessionProviderProps {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
/** JWT token (required to connect; null = no connection) */
|
|
12
|
+
token: string | null;
|
|
13
|
+
/** WebSocket / API base URL for Socket.IO (e.g. https://portal.example.com) */
|
|
14
|
+
wsUrl: string;
|
|
15
|
+
/** Current user ID (for filtering session_revoked events) */
|
|
16
|
+
userId?: string | null;
|
|
17
|
+
/** localStorage key for token (for cross-tab sync via storage event) */
|
|
18
|
+
tokenStorageKey?: string;
|
|
19
|
+
/** Callback to refresh token - return new token string */
|
|
20
|
+
onTokenRefresh?: () => Promise<string>;
|
|
21
|
+
/** Called when session is definitely gone (expired or revoked) */
|
|
22
|
+
onSessionEnd?: () => void;
|
|
23
|
+
}
|
|
24
|
+
interface SessionContextValue {
|
|
25
|
+
isConnected: boolean;
|
|
26
|
+
sessionState: SessionState;
|
|
27
|
+
remainingSeconds: number | null;
|
|
28
|
+
expiringData: {
|
|
29
|
+
remaining_seconds: number;
|
|
30
|
+
reason: string;
|
|
31
|
+
} | null;
|
|
32
|
+
expiredData: {
|
|
33
|
+
session_id: string;
|
|
34
|
+
reason: string;
|
|
35
|
+
} | null;
|
|
36
|
+
revokedData: {
|
|
37
|
+
session_id: string;
|
|
38
|
+
reason: string;
|
|
39
|
+
revoked_by?: string;
|
|
40
|
+
user_id?: string;
|
|
41
|
+
} | null;
|
|
42
|
+
/** Send heartbeat + refresh token (for "continue" button) */
|
|
43
|
+
continueSession: () => Promise<void>;
|
|
44
|
+
/** Force send heartbeat only */
|
|
45
|
+
sendHeartbeat: () => void;
|
|
46
|
+
}
|
|
47
|
+
/** Options for useSessionEvents (lower-level hook) */
|
|
48
|
+
interface UseSessionEventsOptions {
|
|
49
|
+
token: string | null;
|
|
50
|
+
wsUrl: string;
|
|
51
|
+
onSessionExpiring?: (data: {
|
|
52
|
+
remaining_seconds: number;
|
|
53
|
+
reason: string;
|
|
54
|
+
}) => void;
|
|
55
|
+
onSessionExpired?: (data: {
|
|
56
|
+
session_id: string;
|
|
57
|
+
reason: string;
|
|
58
|
+
}) => void;
|
|
59
|
+
onSessionRevoked?: (data: {
|
|
60
|
+
session_id: string;
|
|
61
|
+
reason: string;
|
|
62
|
+
revoked_by?: string;
|
|
63
|
+
user_id?: string;
|
|
64
|
+
}) => void;
|
|
65
|
+
onHeartbeatAck?: (data: {
|
|
66
|
+
remaining_seconds: number;
|
|
67
|
+
}) => void;
|
|
68
|
+
/** localStorage key for token (cross-tab sync) */
|
|
69
|
+
tokenStorageKey?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare function SessionProvider({ children, token, wsUrl, userId, tokenStorageKey, onTokenRefresh, onSessionEnd, }: SessionProviderProps): react_jsx_runtime.JSX.Element;
|
|
73
|
+
|
|
74
|
+
declare const SessionContext: react.Context<SessionContextValue | null>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Consume session state and actions from SessionProvider.
|
|
78
|
+
*
|
|
79
|
+
* Must be used within a SessionProvider.
|
|
80
|
+
*/
|
|
81
|
+
declare function useSession(): SessionContextValue;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Lower-level hook that wraps SessionSocket from auth-sdk.
|
|
85
|
+
*
|
|
86
|
+
* - Creates SessionSocket and connects when token + wsUrl are set
|
|
87
|
+
* - Registers DOM activity listeners that call notifyActivity() (activity flag pattern)
|
|
88
|
+
* - Listens for storage events to sync token across tabs (updateToken)
|
|
89
|
+
* - Returns isConnected and sendHeartbeat (forceHeartbeat)
|
|
90
|
+
*
|
|
91
|
+
* Use SessionProvider + useSession for the recommended high-level API.
|
|
92
|
+
*/
|
|
93
|
+
declare function useSessionEvents(options: UseSessionEventsOptions): {
|
|
94
|
+
isConnected: boolean;
|
|
95
|
+
sendHeartbeat: () => void;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export { SessionContext, type SessionContextValue, SessionProvider, type SessionProviderProps, type SessionState, type UseSessionEventsOptions, useSession, useSessionEvents };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Session state machine: idle (no token/not connected) -> active -> expiring -> expired | revoked
|
|
7
|
+
*/
|
|
8
|
+
type SessionState = 'idle' | 'active' | 'expiring' | 'expired' | 'revoked';
|
|
9
|
+
interface SessionProviderProps {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
/** JWT token (required to connect; null = no connection) */
|
|
12
|
+
token: string | null;
|
|
13
|
+
/** WebSocket / API base URL for Socket.IO (e.g. https://portal.example.com) */
|
|
14
|
+
wsUrl: string;
|
|
15
|
+
/** Current user ID (for filtering session_revoked events) */
|
|
16
|
+
userId?: string | null;
|
|
17
|
+
/** localStorage key for token (for cross-tab sync via storage event) */
|
|
18
|
+
tokenStorageKey?: string;
|
|
19
|
+
/** Callback to refresh token - return new token string */
|
|
20
|
+
onTokenRefresh?: () => Promise<string>;
|
|
21
|
+
/** Called when session is definitely gone (expired or revoked) */
|
|
22
|
+
onSessionEnd?: () => void;
|
|
23
|
+
}
|
|
24
|
+
interface SessionContextValue {
|
|
25
|
+
isConnected: boolean;
|
|
26
|
+
sessionState: SessionState;
|
|
27
|
+
remainingSeconds: number | null;
|
|
28
|
+
expiringData: {
|
|
29
|
+
remaining_seconds: number;
|
|
30
|
+
reason: string;
|
|
31
|
+
} | null;
|
|
32
|
+
expiredData: {
|
|
33
|
+
session_id: string;
|
|
34
|
+
reason: string;
|
|
35
|
+
} | null;
|
|
36
|
+
revokedData: {
|
|
37
|
+
session_id: string;
|
|
38
|
+
reason: string;
|
|
39
|
+
revoked_by?: string;
|
|
40
|
+
user_id?: string;
|
|
41
|
+
} | null;
|
|
42
|
+
/** Send heartbeat + refresh token (for "continue" button) */
|
|
43
|
+
continueSession: () => Promise<void>;
|
|
44
|
+
/** Force send heartbeat only */
|
|
45
|
+
sendHeartbeat: () => void;
|
|
46
|
+
}
|
|
47
|
+
/** Options for useSessionEvents (lower-level hook) */
|
|
48
|
+
interface UseSessionEventsOptions {
|
|
49
|
+
token: string | null;
|
|
50
|
+
wsUrl: string;
|
|
51
|
+
onSessionExpiring?: (data: {
|
|
52
|
+
remaining_seconds: number;
|
|
53
|
+
reason: string;
|
|
54
|
+
}) => void;
|
|
55
|
+
onSessionExpired?: (data: {
|
|
56
|
+
session_id: string;
|
|
57
|
+
reason: string;
|
|
58
|
+
}) => void;
|
|
59
|
+
onSessionRevoked?: (data: {
|
|
60
|
+
session_id: string;
|
|
61
|
+
reason: string;
|
|
62
|
+
revoked_by?: string;
|
|
63
|
+
user_id?: string;
|
|
64
|
+
}) => void;
|
|
65
|
+
onHeartbeatAck?: (data: {
|
|
66
|
+
remaining_seconds: number;
|
|
67
|
+
}) => void;
|
|
68
|
+
/** localStorage key for token (cross-tab sync) */
|
|
69
|
+
tokenStorageKey?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare function SessionProvider({ children, token, wsUrl, userId, tokenStorageKey, onTokenRefresh, onSessionEnd, }: SessionProviderProps): react_jsx_runtime.JSX.Element;
|
|
73
|
+
|
|
74
|
+
declare const SessionContext: react.Context<SessionContextValue | null>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Consume session state and actions from SessionProvider.
|
|
78
|
+
*
|
|
79
|
+
* Must be used within a SessionProvider.
|
|
80
|
+
*/
|
|
81
|
+
declare function useSession(): SessionContextValue;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Lower-level hook that wraps SessionSocket from auth-sdk.
|
|
85
|
+
*
|
|
86
|
+
* - Creates SessionSocket and connects when token + wsUrl are set
|
|
87
|
+
* - Registers DOM activity listeners that call notifyActivity() (activity flag pattern)
|
|
88
|
+
* - Listens for storage events to sync token across tabs (updateToken)
|
|
89
|
+
* - Returns isConnected and sendHeartbeat (forceHeartbeat)
|
|
90
|
+
*
|
|
91
|
+
* Use SessionProvider + useSession for the recommended high-level API.
|
|
92
|
+
*/
|
|
93
|
+
declare function useSessionEvents(options: UseSessionEventsOptions): {
|
|
94
|
+
isConnected: boolean;
|
|
95
|
+
sendHeartbeat: () => void;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export { SessionContext, type SessionContextValue, SessionProvider, type SessionProviderProps, type SessionState, type UseSessionEventsOptions, useSession, useSessionEvents };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
SessionContext: () => SessionContext,
|
|
24
|
+
SessionProvider: () => SessionProvider,
|
|
25
|
+
useSession: () => useSession,
|
|
26
|
+
useSessionEvents: () => useSessionEvents
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/SessionProvider.tsx
|
|
31
|
+
var import_react3 = require("react");
|
|
32
|
+
var import_win_portal_auth_sdk = require("win-portal-auth-sdk");
|
|
33
|
+
|
|
34
|
+
// src/SessionContext.ts
|
|
35
|
+
var import_react = require("react");
|
|
36
|
+
var SessionContext = (0, import_react.createContext)(null);
|
|
37
|
+
|
|
38
|
+
// src/useSessionEvents.ts
|
|
39
|
+
var import_react2 = require("react");
|
|
40
|
+
var import_client = require("win-portal-auth-sdk/client");
|
|
41
|
+
var ACTIVITY_EVENTS = ["mousemove", "keydown", "touchstart", "scroll", "click"];
|
|
42
|
+
function useSessionEvents(options) {
|
|
43
|
+
const {
|
|
44
|
+
token,
|
|
45
|
+
wsUrl,
|
|
46
|
+
onSessionExpiring,
|
|
47
|
+
onSessionExpired,
|
|
48
|
+
onSessionRevoked,
|
|
49
|
+
onHeartbeatAck,
|
|
50
|
+
tokenStorageKey
|
|
51
|
+
} = options;
|
|
52
|
+
const [isConnected, setIsConnected] = (0, import_react2.useState)(false);
|
|
53
|
+
const socketRef = (0, import_react2.useRef)(null);
|
|
54
|
+
const optionsRef = (0, import_react2.useRef)(options);
|
|
55
|
+
optionsRef.current = options;
|
|
56
|
+
const baseURL = wsUrl.replace(/\/$/, "");
|
|
57
|
+
(0, import_react2.useEffect)(() => {
|
|
58
|
+
if (!token || !baseURL) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const opts = optionsRef.current;
|
|
62
|
+
const socket = new import_client.SessionSocket({
|
|
63
|
+
baseURL,
|
|
64
|
+
token,
|
|
65
|
+
namespace: "/app-sessions",
|
|
66
|
+
onSessionExpired: (data) => opts.onSessionExpired?.(data),
|
|
67
|
+
onSessionRevoked: (data) => opts.onSessionRevoked?.(data),
|
|
68
|
+
onSessionExpiring: (data) => opts.onSessionExpiring?.(data),
|
|
69
|
+
onConnectionChange: (connected) => setIsConnected(connected),
|
|
70
|
+
onHeartbeatAck: (data) => opts.onHeartbeatAck?.(data),
|
|
71
|
+
autoReconnect: true,
|
|
72
|
+
transports: ["websocket"]
|
|
73
|
+
});
|
|
74
|
+
socketRef.current = socket;
|
|
75
|
+
socket.connect().catch(() => {
|
|
76
|
+
setIsConnected(false);
|
|
77
|
+
});
|
|
78
|
+
return () => {
|
|
79
|
+
socket.disconnect();
|
|
80
|
+
socketRef.current = null;
|
|
81
|
+
setIsConnected(false);
|
|
82
|
+
};
|
|
83
|
+
}, [baseURL, token]);
|
|
84
|
+
(0, import_react2.useEffect)(() => {
|
|
85
|
+
if (!token || !socketRef.current) return;
|
|
86
|
+
socketRef.current.updateToken(token);
|
|
87
|
+
}, [token]);
|
|
88
|
+
(0, import_react2.useEffect)(() => {
|
|
89
|
+
if (!isConnected) return;
|
|
90
|
+
const socket = socketRef.current;
|
|
91
|
+
if (!socket) return;
|
|
92
|
+
const markActivity = () => socket.notifyActivity();
|
|
93
|
+
ACTIVITY_EVENTS.forEach((event) => {
|
|
94
|
+
window.addEventListener(event, markActivity, { passive: true });
|
|
95
|
+
});
|
|
96
|
+
return () => {
|
|
97
|
+
ACTIVITY_EVENTS.forEach((event) => {
|
|
98
|
+
window.removeEventListener(event, markActivity);
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
}, [isConnected]);
|
|
102
|
+
(0, import_react2.useEffect)(() => {
|
|
103
|
+
if (!isConnected || !tokenStorageKey) return;
|
|
104
|
+
const handleStorage = (e) => {
|
|
105
|
+
if (e.key === tokenStorageKey && e.newValue) {
|
|
106
|
+
socketRef.current?.updateToken(e.newValue);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
window.addEventListener("storage", handleStorage);
|
|
110
|
+
return () => window.removeEventListener("storage", handleStorage);
|
|
111
|
+
}, [isConnected, tokenStorageKey]);
|
|
112
|
+
const sendHeartbeat = (0, import_react2.useCallback)(() => {
|
|
113
|
+
socketRef.current?.forceHeartbeat();
|
|
114
|
+
}, []);
|
|
115
|
+
return { isConnected, sendHeartbeat };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/SessionProvider.tsx
|
|
119
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
120
|
+
function getSessionIdFromToken(token) {
|
|
121
|
+
if (!token) return null;
|
|
122
|
+
const payload = (0, import_win_portal_auth_sdk.decodeJwt)(token);
|
|
123
|
+
return payload?.sub ?? null;
|
|
124
|
+
}
|
|
125
|
+
function SessionProvider({
|
|
126
|
+
children,
|
|
127
|
+
token,
|
|
128
|
+
wsUrl,
|
|
129
|
+
userId = null,
|
|
130
|
+
tokenStorageKey,
|
|
131
|
+
onTokenRefresh,
|
|
132
|
+
onSessionEnd
|
|
133
|
+
}) {
|
|
134
|
+
const [sessionState, setSessionState] = (0, import_react3.useState)(token && wsUrl ? "active" : "idle");
|
|
135
|
+
const [remainingSeconds, setRemainingSeconds] = (0, import_react3.useState)(null);
|
|
136
|
+
const [expiringData, setExpiringData] = (0, import_react3.useState)(null);
|
|
137
|
+
const [expiredData, setExpiredData] = (0, import_react3.useState)(null);
|
|
138
|
+
const [revokedData, setRevokedData] = (0, import_react3.useState)(null);
|
|
139
|
+
const onSessionEndRef = (0, import_react3.useRef)(onSessionEnd);
|
|
140
|
+
onSessionEndRef.current = onSessionEnd;
|
|
141
|
+
const onTokenRefreshRef = (0, import_react3.useRef)(onTokenRefresh);
|
|
142
|
+
onTokenRefreshRef.current = onTokenRefresh;
|
|
143
|
+
const userIdRef = (0, import_react3.useRef)(userId);
|
|
144
|
+
userIdRef.current = userId;
|
|
145
|
+
const handleSessionExpiring = (0, import_react3.useCallback)((data) => {
|
|
146
|
+
setExpiringData(data);
|
|
147
|
+
setRemainingSeconds(data.remaining_seconds);
|
|
148
|
+
setSessionState("expiring");
|
|
149
|
+
}, []);
|
|
150
|
+
const handleSessionExpired = (0, import_react3.useCallback)((data) => {
|
|
151
|
+
setExpiredData(data);
|
|
152
|
+
setSessionState("expired");
|
|
153
|
+
setExpiringData(null);
|
|
154
|
+
setRemainingSeconds(null);
|
|
155
|
+
onSessionEndRef.current?.();
|
|
156
|
+
}, []);
|
|
157
|
+
const handleSessionRevoked = (0, import_react3.useCallback)(
|
|
158
|
+
(data) => {
|
|
159
|
+
const currentUserId = userIdRef.current;
|
|
160
|
+
const currentSessionId = getSessionIdFromToken(token);
|
|
161
|
+
if (currentUserId != null && data.user_id != null && data.user_id !== currentUserId) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (currentSessionId != null && data.session_id !== currentSessionId) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
setRevokedData(data);
|
|
168
|
+
setSessionState("revoked");
|
|
169
|
+
setExpiringData(null);
|
|
170
|
+
setExpiredData(null);
|
|
171
|
+
setRemainingSeconds(null);
|
|
172
|
+
onSessionEndRef.current?.();
|
|
173
|
+
},
|
|
174
|
+
[token]
|
|
175
|
+
);
|
|
176
|
+
const { isConnected, sendHeartbeat } = useSessionEvents({
|
|
177
|
+
token,
|
|
178
|
+
wsUrl,
|
|
179
|
+
onSessionExpiring: handleSessionExpiring,
|
|
180
|
+
onSessionExpired: handleSessionExpired,
|
|
181
|
+
onSessionRevoked: handleSessionRevoked,
|
|
182
|
+
tokenStorageKey
|
|
183
|
+
});
|
|
184
|
+
(0, import_react3.useEffect)(() => {
|
|
185
|
+
if (isConnected && sessionState === "idle") {
|
|
186
|
+
setSessionState("active");
|
|
187
|
+
}
|
|
188
|
+
}, [isConnected, sessionState]);
|
|
189
|
+
const continueSession = (0, import_react3.useCallback)(async () => {
|
|
190
|
+
sendHeartbeat();
|
|
191
|
+
const refresh = onTokenRefreshRef.current;
|
|
192
|
+
if (refresh) {
|
|
193
|
+
try {
|
|
194
|
+
await refresh();
|
|
195
|
+
setSessionState("active");
|
|
196
|
+
setExpiringData(null);
|
|
197
|
+
setRemainingSeconds(null);
|
|
198
|
+
} catch {
|
|
199
|
+
setSessionState("expired");
|
|
200
|
+
onSessionEndRef.current?.();
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
setSessionState("active");
|
|
204
|
+
setExpiringData(null);
|
|
205
|
+
setRemainingSeconds(null);
|
|
206
|
+
}
|
|
207
|
+
}, [sendHeartbeat]);
|
|
208
|
+
const value = {
|
|
209
|
+
isConnected,
|
|
210
|
+
sessionState,
|
|
211
|
+
remainingSeconds,
|
|
212
|
+
expiringData,
|
|
213
|
+
expiredData,
|
|
214
|
+
revokedData,
|
|
215
|
+
continueSession,
|
|
216
|
+
sendHeartbeat
|
|
217
|
+
};
|
|
218
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SessionContext.Provider, { value, children });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/useSession.ts
|
|
222
|
+
var import_react4 = require("react");
|
|
223
|
+
function useSession() {
|
|
224
|
+
const context = (0, import_react4.useContext)(SessionContext);
|
|
225
|
+
if (context == null) {
|
|
226
|
+
throw new Error("useSession must be used within a SessionProvider");
|
|
227
|
+
}
|
|
228
|
+
return context;
|
|
229
|
+
}
|
|
230
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
231
|
+
0 && (module.exports = {
|
|
232
|
+
SessionContext,
|
|
233
|
+
SessionProvider,
|
|
234
|
+
useSession,
|
|
235
|
+
useSessionEvents
|
|
236
|
+
});
|
|
237
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/SessionProvider.tsx","../src/SessionContext.ts","../src/useSessionEvents.ts","../src/useSession.ts"],"sourcesContent":["export { SessionProvider } from './SessionProvider';\nexport { SessionContext } from './SessionContext';\nexport { useSession } from './useSession';\nexport { useSessionEvents } from './useSessionEvents';\nexport type {\n SessionState,\n SessionProviderProps,\n SessionContextValue,\n UseSessionEventsOptions,\n} from './types';\n","'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { decodeJwt } from 'win-portal-auth-sdk';\nimport { SessionContext } from './SessionContext';\nimport { useSessionEvents } from './useSessionEvents';\nimport type { SessionContextValue, SessionProviderProps, SessionState } from './types';\n\nfunction getSessionIdFromToken(token: string | null): string | null {\n if (!token) return null;\n const payload = decodeJwt(token);\n return payload?.sub ?? null;\n}\n\nexport function SessionProvider({\n children,\n token,\n wsUrl,\n userId = null,\n tokenStorageKey,\n onTokenRefresh,\n onSessionEnd,\n}: SessionProviderProps) {\n const [sessionState, setSessionState] = useState<SessionState>(token && wsUrl ? 'active' : 'idle');\n const [remainingSeconds, setRemainingSeconds] = useState<number | null>(null);\n const [expiringData, setExpiringData] = useState<{\n remaining_seconds: number;\n reason: string;\n } | null>(null);\n const [expiredData, setExpiredData] = useState<{ session_id: string; reason: string } | null>(null);\n const [revokedData, setRevokedData] = useState<{\n session_id: string;\n reason: string;\n revoked_by?: string;\n user_id?: string;\n } | null>(null);\n\n const onSessionEndRef = useRef(onSessionEnd);\n onSessionEndRef.current = onSessionEnd;\n const onTokenRefreshRef = useRef(onTokenRefresh);\n onTokenRefreshRef.current = onTokenRefresh;\n const userIdRef = useRef(userId);\n userIdRef.current = userId;\n\n const handleSessionExpiring = useCallback((data: { remaining_seconds: number; reason: string }) => {\n setExpiringData(data);\n setRemainingSeconds(data.remaining_seconds);\n setSessionState('expiring');\n }, []);\n\n const handleSessionExpired = useCallback((data: { session_id: string; reason: string }) => {\n setExpiredData(data);\n setSessionState('expired');\n setExpiringData(null);\n setRemainingSeconds(null);\n onSessionEndRef.current?.();\n }, []);\n\n const handleSessionRevoked = useCallback(\n (data: { session_id: string; reason: string; revoked_by?: string; user_id?: string }) => {\n const currentUserId = userIdRef.current;\n const currentSessionId = getSessionIdFromToken(token);\n\n if (currentUserId != null && data.user_id != null && data.user_id !== currentUserId) {\n return;\n }\n if (currentSessionId != null && data.session_id !== currentSessionId) {\n return;\n }\n\n setRevokedData(data);\n setSessionState('revoked');\n setExpiringData(null);\n setExpiredData(null);\n setRemainingSeconds(null);\n onSessionEndRef.current?.();\n },\n [token]\n );\n\n const { isConnected, sendHeartbeat } = useSessionEvents({\n token,\n wsUrl,\n onSessionExpiring: handleSessionExpiring,\n onSessionExpired: handleSessionExpired,\n onSessionRevoked: handleSessionRevoked,\n tokenStorageKey,\n });\n\n // When connection is established and we were idle, move to active\n useEffect(() => {\n if (isConnected && sessionState === 'idle') {\n setSessionState('active');\n }\n }, [isConnected, sessionState]);\n\n const continueSession = useCallback(async () => {\n sendHeartbeat();\n const refresh = onTokenRefreshRef.current;\n if (refresh) {\n try {\n await refresh();\n setSessionState('active');\n setExpiringData(null);\n setRemainingSeconds(null);\n } catch {\n setSessionState('expired');\n onSessionEndRef.current?.();\n }\n } else {\n setSessionState('active');\n setExpiringData(null);\n setRemainingSeconds(null);\n }\n }, [sendHeartbeat]);\n\n const value: SessionContextValue = {\n isConnected,\n sessionState,\n remainingSeconds,\n expiringData,\n expiredData,\n revokedData,\n continueSession,\n sendHeartbeat,\n };\n\n return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;\n}\n","import { createContext } from 'react';\nimport type { SessionContextValue } from './types';\n\nexport const SessionContext = createContext<SessionContextValue | null>(null);\n","'use client';\n\nimport { useEffect, useCallback, useRef, useState } from 'react';\nimport { SessionSocket } from 'win-portal-auth-sdk/client';\nimport type { UseSessionEventsOptions } from './types';\n\nconst ACTIVITY_EVENTS = ['mousemove', 'keydown', 'touchstart', 'scroll', 'click'] as const;\n\n/**\n * Lower-level hook that wraps SessionSocket from auth-sdk.\n *\n * - Creates SessionSocket and connects when token + wsUrl are set\n * - Registers DOM activity listeners that call notifyActivity() (activity flag pattern)\n * - Listens for storage events to sync token across tabs (updateToken)\n * - Returns isConnected and sendHeartbeat (forceHeartbeat)\n *\n * Use SessionProvider + useSession for the recommended high-level API.\n */\nexport function useSessionEvents(options: UseSessionEventsOptions) {\n const {\n token,\n wsUrl,\n onSessionExpiring,\n onSessionExpired,\n onSessionRevoked,\n onHeartbeatAck,\n tokenStorageKey,\n } = options;\n\n const [isConnected, setIsConnected] = useState(false);\n const socketRef = useRef<SessionSocket | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const baseURL = wsUrl.replace(/\\/$/, '');\n\n // Create SessionSocket and connect when token + wsUrl available\n useEffect(() => {\n if (!token || !baseURL) {\n return;\n }\n\n const opts = optionsRef.current;\n const socket = new SessionSocket({\n baseURL,\n token,\n namespace: '/app-sessions',\n onSessionExpired: (data) => opts.onSessionExpired?.(data),\n onSessionRevoked: (data) => opts.onSessionRevoked?.(data),\n onSessionExpiring: (data) => opts.onSessionExpiring?.(data),\n onConnectionChange: (connected) => setIsConnected(connected),\n onHeartbeatAck: (data) => opts.onHeartbeatAck?.(data),\n autoReconnect: true,\n transports: ['websocket'],\n });\n\n socketRef.current = socket;\n socket.connect().catch(() => {\n setIsConnected(false);\n });\n\n return () => {\n socket.disconnect();\n socketRef.current = null;\n setIsConnected(false);\n };\n }, [baseURL, token]);\n\n // When token changes (e.g. refresh), update socket auth for next reconnection\n useEffect(() => {\n if (!token || !socketRef.current) return;\n socketRef.current.updateToken(token);\n }, [token]);\n\n // DOM activity listeners: call notifyActivity() (ultra cheap flag)\n useEffect(() => {\n if (!isConnected) return;\n const socket = socketRef.current;\n if (!socket) return;\n\n const markActivity = () => socket.notifyActivity();\n ACTIVITY_EVENTS.forEach((event) => {\n window.addEventListener(event, markActivity, { passive: true });\n });\n return () => {\n ACTIVITY_EVENTS.forEach((event) => {\n window.removeEventListener(event, markActivity);\n });\n };\n }, [isConnected]);\n\n // Storage event: sync token across tabs\n useEffect(() => {\n if (!isConnected || !tokenStorageKey) return;\n\n const handleStorage = (e: StorageEvent) => {\n if (e.key === tokenStorageKey && e.newValue) {\n socketRef.current?.updateToken(e.newValue);\n }\n };\n window.addEventListener('storage', handleStorage);\n return () => window.removeEventListener('storage', handleStorage);\n }, [isConnected, tokenStorageKey]);\n\n const sendHeartbeat = useCallback(() => {\n socketRef.current?.forceHeartbeat();\n }, []);\n\n return { isConnected, sendHeartbeat };\n}\n","'use client';\n\nimport { useContext } from 'react';\nimport { SessionContext } from './SessionContext';\nimport type { SessionContextValue } from './types';\n\n/**\n * Consume session state and actions from SessionProvider.\n *\n * Must be used within a SessionProvider.\n */\nexport function useSession(): SessionContextValue {\n const context = useContext(SessionContext);\n if (context == null) {\n throw new Error('useSession must be used within a SessionProvider');\n }\n return context;\n}\n"],"mappings":"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","names":["import_react","import_react","import_react"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// src/SessionProvider.tsx
|
|
2
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
3
|
+
import { decodeJwt } from "win-portal-auth-sdk";
|
|
4
|
+
|
|
5
|
+
// src/SessionContext.ts
|
|
6
|
+
import { createContext } from "react";
|
|
7
|
+
var SessionContext = createContext(null);
|
|
8
|
+
|
|
9
|
+
// src/useSessionEvents.ts
|
|
10
|
+
import { useEffect, useCallback, useRef, useState } from "react";
|
|
11
|
+
import { SessionSocket } from "win-portal-auth-sdk/client";
|
|
12
|
+
var ACTIVITY_EVENTS = ["mousemove", "keydown", "touchstart", "scroll", "click"];
|
|
13
|
+
function useSessionEvents(options) {
|
|
14
|
+
const {
|
|
15
|
+
token,
|
|
16
|
+
wsUrl,
|
|
17
|
+
onSessionExpiring,
|
|
18
|
+
onSessionExpired,
|
|
19
|
+
onSessionRevoked,
|
|
20
|
+
onHeartbeatAck,
|
|
21
|
+
tokenStorageKey
|
|
22
|
+
} = options;
|
|
23
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
24
|
+
const socketRef = useRef(null);
|
|
25
|
+
const optionsRef = useRef(options);
|
|
26
|
+
optionsRef.current = options;
|
|
27
|
+
const baseURL = wsUrl.replace(/\/$/, "");
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!token || !baseURL) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const opts = optionsRef.current;
|
|
33
|
+
const socket = new SessionSocket({
|
|
34
|
+
baseURL,
|
|
35
|
+
token,
|
|
36
|
+
namespace: "/app-sessions",
|
|
37
|
+
onSessionExpired: (data) => opts.onSessionExpired?.(data),
|
|
38
|
+
onSessionRevoked: (data) => opts.onSessionRevoked?.(data),
|
|
39
|
+
onSessionExpiring: (data) => opts.onSessionExpiring?.(data),
|
|
40
|
+
onConnectionChange: (connected) => setIsConnected(connected),
|
|
41
|
+
onHeartbeatAck: (data) => opts.onHeartbeatAck?.(data),
|
|
42
|
+
autoReconnect: true,
|
|
43
|
+
transports: ["websocket"]
|
|
44
|
+
});
|
|
45
|
+
socketRef.current = socket;
|
|
46
|
+
socket.connect().catch(() => {
|
|
47
|
+
setIsConnected(false);
|
|
48
|
+
});
|
|
49
|
+
return () => {
|
|
50
|
+
socket.disconnect();
|
|
51
|
+
socketRef.current = null;
|
|
52
|
+
setIsConnected(false);
|
|
53
|
+
};
|
|
54
|
+
}, [baseURL, token]);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!token || !socketRef.current) return;
|
|
57
|
+
socketRef.current.updateToken(token);
|
|
58
|
+
}, [token]);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!isConnected) return;
|
|
61
|
+
const socket = socketRef.current;
|
|
62
|
+
if (!socket) return;
|
|
63
|
+
const markActivity = () => socket.notifyActivity();
|
|
64
|
+
ACTIVITY_EVENTS.forEach((event) => {
|
|
65
|
+
window.addEventListener(event, markActivity, { passive: true });
|
|
66
|
+
});
|
|
67
|
+
return () => {
|
|
68
|
+
ACTIVITY_EVENTS.forEach((event) => {
|
|
69
|
+
window.removeEventListener(event, markActivity);
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
}, [isConnected]);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!isConnected || !tokenStorageKey) return;
|
|
75
|
+
const handleStorage = (e) => {
|
|
76
|
+
if (e.key === tokenStorageKey && e.newValue) {
|
|
77
|
+
socketRef.current?.updateToken(e.newValue);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
window.addEventListener("storage", handleStorage);
|
|
81
|
+
return () => window.removeEventListener("storage", handleStorage);
|
|
82
|
+
}, [isConnected, tokenStorageKey]);
|
|
83
|
+
const sendHeartbeat = useCallback(() => {
|
|
84
|
+
socketRef.current?.forceHeartbeat();
|
|
85
|
+
}, []);
|
|
86
|
+
return { isConnected, sendHeartbeat };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/SessionProvider.tsx
|
|
90
|
+
import { jsx } from "react/jsx-runtime";
|
|
91
|
+
function getSessionIdFromToken(token) {
|
|
92
|
+
if (!token) return null;
|
|
93
|
+
const payload = decodeJwt(token);
|
|
94
|
+
return payload?.sub ?? null;
|
|
95
|
+
}
|
|
96
|
+
function SessionProvider({
|
|
97
|
+
children,
|
|
98
|
+
token,
|
|
99
|
+
wsUrl,
|
|
100
|
+
userId = null,
|
|
101
|
+
tokenStorageKey,
|
|
102
|
+
onTokenRefresh,
|
|
103
|
+
onSessionEnd
|
|
104
|
+
}) {
|
|
105
|
+
const [sessionState, setSessionState] = useState2(token && wsUrl ? "active" : "idle");
|
|
106
|
+
const [remainingSeconds, setRemainingSeconds] = useState2(null);
|
|
107
|
+
const [expiringData, setExpiringData] = useState2(null);
|
|
108
|
+
const [expiredData, setExpiredData] = useState2(null);
|
|
109
|
+
const [revokedData, setRevokedData] = useState2(null);
|
|
110
|
+
const onSessionEndRef = useRef2(onSessionEnd);
|
|
111
|
+
onSessionEndRef.current = onSessionEnd;
|
|
112
|
+
const onTokenRefreshRef = useRef2(onTokenRefresh);
|
|
113
|
+
onTokenRefreshRef.current = onTokenRefresh;
|
|
114
|
+
const userIdRef = useRef2(userId);
|
|
115
|
+
userIdRef.current = userId;
|
|
116
|
+
const handleSessionExpiring = useCallback2((data) => {
|
|
117
|
+
setExpiringData(data);
|
|
118
|
+
setRemainingSeconds(data.remaining_seconds);
|
|
119
|
+
setSessionState("expiring");
|
|
120
|
+
}, []);
|
|
121
|
+
const handleSessionExpired = useCallback2((data) => {
|
|
122
|
+
setExpiredData(data);
|
|
123
|
+
setSessionState("expired");
|
|
124
|
+
setExpiringData(null);
|
|
125
|
+
setRemainingSeconds(null);
|
|
126
|
+
onSessionEndRef.current?.();
|
|
127
|
+
}, []);
|
|
128
|
+
const handleSessionRevoked = useCallback2(
|
|
129
|
+
(data) => {
|
|
130
|
+
const currentUserId = userIdRef.current;
|
|
131
|
+
const currentSessionId = getSessionIdFromToken(token);
|
|
132
|
+
if (currentUserId != null && data.user_id != null && data.user_id !== currentUserId) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (currentSessionId != null && data.session_id !== currentSessionId) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
setRevokedData(data);
|
|
139
|
+
setSessionState("revoked");
|
|
140
|
+
setExpiringData(null);
|
|
141
|
+
setExpiredData(null);
|
|
142
|
+
setRemainingSeconds(null);
|
|
143
|
+
onSessionEndRef.current?.();
|
|
144
|
+
},
|
|
145
|
+
[token]
|
|
146
|
+
);
|
|
147
|
+
const { isConnected, sendHeartbeat } = useSessionEvents({
|
|
148
|
+
token,
|
|
149
|
+
wsUrl,
|
|
150
|
+
onSessionExpiring: handleSessionExpiring,
|
|
151
|
+
onSessionExpired: handleSessionExpired,
|
|
152
|
+
onSessionRevoked: handleSessionRevoked,
|
|
153
|
+
tokenStorageKey
|
|
154
|
+
});
|
|
155
|
+
useEffect2(() => {
|
|
156
|
+
if (isConnected && sessionState === "idle") {
|
|
157
|
+
setSessionState("active");
|
|
158
|
+
}
|
|
159
|
+
}, [isConnected, sessionState]);
|
|
160
|
+
const continueSession = useCallback2(async () => {
|
|
161
|
+
sendHeartbeat();
|
|
162
|
+
const refresh = onTokenRefreshRef.current;
|
|
163
|
+
if (refresh) {
|
|
164
|
+
try {
|
|
165
|
+
await refresh();
|
|
166
|
+
setSessionState("active");
|
|
167
|
+
setExpiringData(null);
|
|
168
|
+
setRemainingSeconds(null);
|
|
169
|
+
} catch {
|
|
170
|
+
setSessionState("expired");
|
|
171
|
+
onSessionEndRef.current?.();
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
setSessionState("active");
|
|
175
|
+
setExpiringData(null);
|
|
176
|
+
setRemainingSeconds(null);
|
|
177
|
+
}
|
|
178
|
+
}, [sendHeartbeat]);
|
|
179
|
+
const value = {
|
|
180
|
+
isConnected,
|
|
181
|
+
sessionState,
|
|
182
|
+
remainingSeconds,
|
|
183
|
+
expiringData,
|
|
184
|
+
expiredData,
|
|
185
|
+
revokedData,
|
|
186
|
+
continueSession,
|
|
187
|
+
sendHeartbeat
|
|
188
|
+
};
|
|
189
|
+
return /* @__PURE__ */ jsx(SessionContext.Provider, { value, children });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/useSession.ts
|
|
193
|
+
import { useContext } from "react";
|
|
194
|
+
function useSession() {
|
|
195
|
+
const context = useContext(SessionContext);
|
|
196
|
+
if (context == null) {
|
|
197
|
+
throw new Error("useSession must be used within a SessionProvider");
|
|
198
|
+
}
|
|
199
|
+
return context;
|
|
200
|
+
}
|
|
201
|
+
export {
|
|
202
|
+
SessionContext,
|
|
203
|
+
SessionProvider,
|
|
204
|
+
useSession,
|
|
205
|
+
useSessionEvents
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SessionProvider.tsx","../src/SessionContext.ts","../src/useSessionEvents.ts","../src/useSession.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { decodeJwt } from 'win-portal-auth-sdk';\nimport { SessionContext } from './SessionContext';\nimport { useSessionEvents } from './useSessionEvents';\nimport type { SessionContextValue, SessionProviderProps, SessionState } from './types';\n\nfunction getSessionIdFromToken(token: string | null): string | null {\n if (!token) return null;\n const payload = decodeJwt(token);\n return payload?.sub ?? null;\n}\n\nexport function SessionProvider({\n children,\n token,\n wsUrl,\n userId = null,\n tokenStorageKey,\n onTokenRefresh,\n onSessionEnd,\n}: SessionProviderProps) {\n const [sessionState, setSessionState] = useState<SessionState>(token && wsUrl ? 'active' : 'idle');\n const [remainingSeconds, setRemainingSeconds] = useState<number | null>(null);\n const [expiringData, setExpiringData] = useState<{\n remaining_seconds: number;\n reason: string;\n } | null>(null);\n const [expiredData, setExpiredData] = useState<{ session_id: string; reason: string } | null>(null);\n const [revokedData, setRevokedData] = useState<{\n session_id: string;\n reason: string;\n revoked_by?: string;\n user_id?: string;\n } | null>(null);\n\n const onSessionEndRef = useRef(onSessionEnd);\n onSessionEndRef.current = onSessionEnd;\n const onTokenRefreshRef = useRef(onTokenRefresh);\n onTokenRefreshRef.current = onTokenRefresh;\n const userIdRef = useRef(userId);\n userIdRef.current = userId;\n\n const handleSessionExpiring = useCallback((data: { remaining_seconds: number; reason: string }) => {\n setExpiringData(data);\n setRemainingSeconds(data.remaining_seconds);\n setSessionState('expiring');\n }, []);\n\n const handleSessionExpired = useCallback((data: { session_id: string; reason: string }) => {\n setExpiredData(data);\n setSessionState('expired');\n setExpiringData(null);\n setRemainingSeconds(null);\n onSessionEndRef.current?.();\n }, []);\n\n const handleSessionRevoked = useCallback(\n (data: { session_id: string; reason: string; revoked_by?: string; user_id?: string }) => {\n const currentUserId = userIdRef.current;\n const currentSessionId = getSessionIdFromToken(token);\n\n if (currentUserId != null && data.user_id != null && data.user_id !== currentUserId) {\n return;\n }\n if (currentSessionId != null && data.session_id !== currentSessionId) {\n return;\n }\n\n setRevokedData(data);\n setSessionState('revoked');\n setExpiringData(null);\n setExpiredData(null);\n setRemainingSeconds(null);\n onSessionEndRef.current?.();\n },\n [token]\n );\n\n const { isConnected, sendHeartbeat } = useSessionEvents({\n token,\n wsUrl,\n onSessionExpiring: handleSessionExpiring,\n onSessionExpired: handleSessionExpired,\n onSessionRevoked: handleSessionRevoked,\n tokenStorageKey,\n });\n\n // When connection is established and we were idle, move to active\n useEffect(() => {\n if (isConnected && sessionState === 'idle') {\n setSessionState('active');\n }\n }, [isConnected, sessionState]);\n\n const continueSession = useCallback(async () => {\n sendHeartbeat();\n const refresh = onTokenRefreshRef.current;\n if (refresh) {\n try {\n await refresh();\n setSessionState('active');\n setExpiringData(null);\n setRemainingSeconds(null);\n } catch {\n setSessionState('expired');\n onSessionEndRef.current?.();\n }\n } else {\n setSessionState('active');\n setExpiringData(null);\n setRemainingSeconds(null);\n }\n }, [sendHeartbeat]);\n\n const value: SessionContextValue = {\n isConnected,\n sessionState,\n remainingSeconds,\n expiringData,\n expiredData,\n revokedData,\n continueSession,\n sendHeartbeat,\n };\n\n return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;\n}\n","import { createContext } from 'react';\nimport type { SessionContextValue } from './types';\n\nexport const SessionContext = createContext<SessionContextValue | null>(null);\n","'use client';\n\nimport { useEffect, useCallback, useRef, useState } from 'react';\nimport { SessionSocket } from 'win-portal-auth-sdk/client';\nimport type { UseSessionEventsOptions } from './types';\n\nconst ACTIVITY_EVENTS = ['mousemove', 'keydown', 'touchstart', 'scroll', 'click'] as const;\n\n/**\n * Lower-level hook that wraps SessionSocket from auth-sdk.\n *\n * - Creates SessionSocket and connects when token + wsUrl are set\n * - Registers DOM activity listeners that call notifyActivity() (activity flag pattern)\n * - Listens for storage events to sync token across tabs (updateToken)\n * - Returns isConnected and sendHeartbeat (forceHeartbeat)\n *\n * Use SessionProvider + useSession for the recommended high-level API.\n */\nexport function useSessionEvents(options: UseSessionEventsOptions) {\n const {\n token,\n wsUrl,\n onSessionExpiring,\n onSessionExpired,\n onSessionRevoked,\n onHeartbeatAck,\n tokenStorageKey,\n } = options;\n\n const [isConnected, setIsConnected] = useState(false);\n const socketRef = useRef<SessionSocket | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const baseURL = wsUrl.replace(/\\/$/, '');\n\n // Create SessionSocket and connect when token + wsUrl available\n useEffect(() => {\n if (!token || !baseURL) {\n return;\n }\n\n const opts = optionsRef.current;\n const socket = new SessionSocket({\n baseURL,\n token,\n namespace: '/app-sessions',\n onSessionExpired: (data) => opts.onSessionExpired?.(data),\n onSessionRevoked: (data) => opts.onSessionRevoked?.(data),\n onSessionExpiring: (data) => opts.onSessionExpiring?.(data),\n onConnectionChange: (connected) => setIsConnected(connected),\n onHeartbeatAck: (data) => opts.onHeartbeatAck?.(data),\n autoReconnect: true,\n transports: ['websocket'],\n });\n\n socketRef.current = socket;\n socket.connect().catch(() => {\n setIsConnected(false);\n });\n\n return () => {\n socket.disconnect();\n socketRef.current = null;\n setIsConnected(false);\n };\n }, [baseURL, token]);\n\n // When token changes (e.g. refresh), update socket auth for next reconnection\n useEffect(() => {\n if (!token || !socketRef.current) return;\n socketRef.current.updateToken(token);\n }, [token]);\n\n // DOM activity listeners: call notifyActivity() (ultra cheap flag)\n useEffect(() => {\n if (!isConnected) return;\n const socket = socketRef.current;\n if (!socket) return;\n\n const markActivity = () => socket.notifyActivity();\n ACTIVITY_EVENTS.forEach((event) => {\n window.addEventListener(event, markActivity, { passive: true });\n });\n return () => {\n ACTIVITY_EVENTS.forEach((event) => {\n window.removeEventListener(event, markActivity);\n });\n };\n }, [isConnected]);\n\n // Storage event: sync token across tabs\n useEffect(() => {\n if (!isConnected || !tokenStorageKey) return;\n\n const handleStorage = (e: StorageEvent) => {\n if (e.key === tokenStorageKey && e.newValue) {\n socketRef.current?.updateToken(e.newValue);\n }\n };\n window.addEventListener('storage', handleStorage);\n return () => window.removeEventListener('storage', handleStorage);\n }, [isConnected, tokenStorageKey]);\n\n const sendHeartbeat = useCallback(() => {\n socketRef.current?.forceHeartbeat();\n }, []);\n\n return { isConnected, sendHeartbeat };\n}\n","'use client';\n\nimport { useContext } from 'react';\nimport { SessionContext } from './SessionContext';\nimport type { SessionContextValue } from './types';\n\n/**\n * Consume session state and actions from SessionProvider.\n *\n * Must be used within a SessionProvider.\n */\nexport function useSession(): SessionContextValue {\n const context = useContext(SessionContext);\n if (context == null) {\n throw new Error('useSession must be used within a SessionProvider');\n }\n return context;\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "win-portal-auth-sdk-react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React bindings for Win Portal Auth SDK - session management with Socket.IO",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
21
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
22
|
+
"socket.io-client": "^4.0.0"
|
|
23
|
+
},
|
|
24
|
+
"peerDependenciesMeta": {
|
|
25
|
+
"socket.io-client": {
|
|
26
|
+
"optional": true
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"win-portal-auth-sdk": "1.6.18"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/react": "^18.2.0",
|
|
34
|
+
"@types/react-dom": "^18.2.0",
|
|
35
|
+
"react": "^18.2.0",
|
|
36
|
+
"react-dom": "^18.2.0",
|
|
37
|
+
"socket.io-client": "^4.7.0",
|
|
38
|
+
"tsup": "^8.0.0",
|
|
39
|
+
"typescript": "^5.3.0"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"auth",
|
|
43
|
+
"session",
|
|
44
|
+
"react",
|
|
45
|
+
"socket.io",
|
|
46
|
+
"win-portal"
|
|
47
|
+
],
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"dev": "tsup --watch",
|
|
55
|
+
"clean": "rm -rf dist"
|
|
56
|
+
}
|
|
57
|
+
}
|