@oxyhq/services 6.10.6 → 6.10.8
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 +2 -2
- package/lib/commonjs/ui/context/OxyContext.js +318 -21
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +9 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +319 -22
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +9 -1
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useSecurityQueries.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useSecurityQueries.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/queries/useSecurityQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ui/context/OxyContext.tsx +326 -19
- package/src/ui/hooks/useSessionManagement.ts +10 -2
|
@@ -21,5 +21,5 @@ export declare const useInfiniteSecurityActivity: (options?: {
|
|
|
21
21
|
limit?: number;
|
|
22
22
|
eventType?: SecurityEventType;
|
|
23
23
|
enabled?: boolean;
|
|
24
|
-
}) => import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/
|
|
24
|
+
}) => import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/query-core").InfiniteData<SecurityActivityResponse, unknown>, Error>;
|
|
25
25
|
//# sourceMappingURL=useSecurityQueries.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSecurityQueries.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/hooks/queries/useSecurityQueries.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEjG;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,UAAU;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,6FA2BF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,yBAAyB,GAAI,cAAU,uFAgBnD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GACtC,UAAU;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,
|
|
1
|
+
{"version":3,"file":"useSecurityQueries.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/hooks/queries/useSecurityQueries.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEjG;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,UAAU;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,6FA2BF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,yBAAyB,GAAI,cAAU,uFAgBnD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GACtC,UAAU;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,kJA4BF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionManagement.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/useSessionManagement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,uBAAuB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,cAAc,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,eAAe,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,WAAW,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;IAC/C,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAKD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,+KAalC,2BAA2B,KAAG,
|
|
1
|
+
{"version":3,"file":"useSessionManagement.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/useSessionManagement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,uBAAuB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,cAAc,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,eAAe,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,WAAW,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;IAC/C,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAKD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,+KAalC,2BAA2B,KAAG,0BAmWhC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"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"}
|
|
@@ -21,5 +21,5 @@ export declare const useInfiniteSecurityActivity: (options?: {
|
|
|
21
21
|
limit?: number;
|
|
22
22
|
eventType?: SecurityEventType;
|
|
23
23
|
enabled?: boolean;
|
|
24
|
-
}) => import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/
|
|
24
|
+
}) => import("@tanstack/react-query").UseInfiniteQueryResult<import("@tanstack/query-core").InfiniteData<SecurityActivityResponse, unknown>, Error>;
|
|
25
25
|
//# sourceMappingURL=useSecurityQueries.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSecurityQueries.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/hooks/queries/useSecurityQueries.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEjG;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,UAAU;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,6FA2BF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,yBAAyB,GAAI,cAAU,uFAgBnD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GACtC,UAAU;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,
|
|
1
|
+
{"version":3,"file":"useSecurityQueries.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/hooks/queries/useSecurityQueries.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEjG;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,UAAU;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,6FA2BF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,yBAAyB,GAAI,cAAU,uFAgBnD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GACtC,UAAU;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,kJA4BF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionManagement.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/useSessionManagement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,uBAAuB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,cAAc,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,eAAe,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,WAAW,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;IAC/C,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAKD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,+KAalC,2BAA2B,KAAG,
|
|
1
|
+
{"version":3,"file":"useSessionManagement.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/useSessionManagement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,uBAAuB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,cAAc,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,eAAe,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,WAAW,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;IAC/C,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAKD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,+KAalC,2BAA2B,KAAG,0BAmWhC,CAAC"}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
useState,
|
|
10
10
|
type ReactNode,
|
|
11
11
|
} from 'react';
|
|
12
|
-
import { OxyServices } from '@oxyhq/core';
|
|
12
|
+
import { OxyServices, oxyClient } from '@oxyhq/core';
|
|
13
13
|
import type { User, ApiError, SessionLoginResponse } from '@oxyhq/core';
|
|
14
14
|
import type { ManagedAccount, CreateManagedAccountInput } from '@oxyhq/core';
|
|
15
15
|
import { KeyManager } from '@oxyhq/core';
|
|
@@ -97,6 +97,77 @@ export interface OxyContextState {
|
|
|
97
97
|
|
|
98
98
|
const OxyContext = createContext<OxyContextState | null>(null);
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Minimal, decode-only shape of the session access-token JWT claims the
|
|
102
|
+
* `POST /auth/refresh` endpoint returns. We only read `sessionId` (and `userId`
|
|
103
|
+
* as a fallback). The token is already signed and verified server-side; the
|
|
104
|
+
* client decodes the payload purely to recover the session id — it does NOT and
|
|
105
|
+
* MUST NOT verify the signature.
|
|
106
|
+
*/
|
|
107
|
+
interface RefreshAccessTokenClaims {
|
|
108
|
+
sessionId?: string;
|
|
109
|
+
userId?: string;
|
|
110
|
+
id?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Decode the payload of a JWT WITHOUT verifying its signature.
|
|
115
|
+
*
|
|
116
|
+
* The server (`POST /auth/refresh`) has already minted and signed this access
|
|
117
|
+
* token; we only need to recover the `sessionId` claim from it on cold boot.
|
|
118
|
+
* Returns `null` for any malformed input rather than throwing, so a bad token
|
|
119
|
+
* simply falls through to the unauthenticated path.
|
|
120
|
+
*
|
|
121
|
+
* Implemented with manual base64url decoding (no `jwt-decode` dependency added
|
|
122
|
+
* to `@oxyhq/services`). Works on web (where this cold-boot path runs) via
|
|
123
|
+
* `atob`; if `atob` is unavailable (non-browser runtime) it is treated as
|
|
124
|
+
* undecodable and returns `null`.
|
|
125
|
+
*/
|
|
126
|
+
function decodeAccessTokenClaims(token: string): RefreshAccessTokenClaims | null {
|
|
127
|
+
if (!token || typeof token !== 'string') {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const segments = token.split('.');
|
|
131
|
+
if (segments.length !== 3) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const payloadSegment = segments[1];
|
|
135
|
+
if (!payloadSegment) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
const base64 = payloadSegment.replace(/-/g, '+').replace(/_/g, '/');
|
|
140
|
+
const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');
|
|
141
|
+
if (typeof atob !== 'function') {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const json = decodeURIComponent(
|
|
145
|
+
atob(padded)
|
|
146
|
+
.split('')
|
|
147
|
+
.map((char) => `%${`00${char.charCodeAt(0).toString(16)}`.slice(-2)}`)
|
|
148
|
+
.join(''),
|
|
149
|
+
);
|
|
150
|
+
const parsed: unknown = JSON.parse(json);
|
|
151
|
+
if (parsed === null || typeof parsed !== 'object') {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const claims = parsed as Record<string, unknown>;
|
|
155
|
+
return {
|
|
156
|
+
sessionId: typeof claims.sessionId === 'string' ? claims.sessionId : undefined,
|
|
157
|
+
userId: typeof claims.userId === 'string' ? claims.userId : undefined,
|
|
158
|
+
id: typeof claims.id === 'string' ? claims.id : undefined,
|
|
159
|
+
};
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Server response shape of `POST /auth/refresh`. */
|
|
166
|
+
interface RefreshCookieResponse {
|
|
167
|
+
accessToken?: string;
|
|
168
|
+
sessionId?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
100
171
|
export interface OxyContextProviderProps {
|
|
101
172
|
children: ReactNode;
|
|
102
173
|
oxyServices?: OxyServices;
|
|
@@ -190,6 +261,43 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
190
261
|
const [initialized, setInitialized] = useState(false);
|
|
191
262
|
const setAuthState = useAuthStore.setState;
|
|
192
263
|
|
|
264
|
+
// Keep the shared `oxyClient` singleton's token store in lockstep with the
|
|
265
|
+
// session owned by THIS provider's instance. Many apps construct their api
|
|
266
|
+
// clients against the exported `oxyClient` singleton (reading
|
|
267
|
+
// `oxyClient.getAccessToken()` to build Authorization headers) while passing
|
|
268
|
+
// only `baseURL` to OxyProvider — in which case the provider owns a DIFFERENT
|
|
269
|
+
// `OxyServices` instance and the singleton would otherwise never receive the
|
|
270
|
+
// token, producing `Authorization: Bearer null`.
|
|
271
|
+
//
|
|
272
|
+
// Subscribing to `onTokensChanged` mirrors EVERY token mutation — sign-in,
|
|
273
|
+
// session restore, switch, silent refresh, and sign-out/clear — onto the
|
|
274
|
+
// singleton at the single source of truth in @oxyhq/core, with no per-app
|
|
275
|
+
// plumbing and regardless of which auth code path fired.
|
|
276
|
+
//
|
|
277
|
+
// When the app passed the singleton itself as `oxyServices` (Mention's
|
|
278
|
+
// pattern), `oxyServices === oxyClient`, so we skip the redundant self-write
|
|
279
|
+
// and the subscription is a no-op mirror — fully backward compatible.
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
if (oxyServices === oxyClient) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const applyToSingleton = (accessToken: string | null) => {
|
|
286
|
+
if (accessToken) {
|
|
287
|
+
oxyClient.setTokens(accessToken);
|
|
288
|
+
} else {
|
|
289
|
+
oxyClient.clearTokens();
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// Seed the singleton with whatever token the instance already holds (it may
|
|
294
|
+
// have been planted synchronously before this effect ran — e.g. a token set
|
|
295
|
+
// during the same tick as mount), then keep it in sync going forward.
|
|
296
|
+
applyToSingleton(oxyServices.getAccessToken());
|
|
297
|
+
|
|
298
|
+
return oxyServices.onTokensChanged(applyToSingleton);
|
|
299
|
+
}, [oxyServices]);
|
|
300
|
+
|
|
193
301
|
const logger = useCallback((message: string, err?: unknown) => {
|
|
194
302
|
if (__DEV__) {
|
|
195
303
|
console.warn(`[OxyContext] ${message}`, err);
|
|
@@ -198,16 +306,58 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
198
306
|
|
|
199
307
|
const storageKeys = useMemo(() => getStorageKeys(storageKeyPrefix), [storageKeyPrefix]);
|
|
200
308
|
|
|
201
|
-
//
|
|
309
|
+
// Storage initialization.
|
|
310
|
+
//
|
|
311
|
+
// `storage` (state) drives render-time gating (`isStorageReady`) and the
|
|
312
|
+
// hooks below. But it is `null` for a brief window after mount while
|
|
313
|
+
// `createPlatformStorage()` resolves (a microtask on web; a dynamic
|
|
314
|
+
// `import()` on native). Any persistence path that fires during that window
|
|
315
|
+
// — e.g. an interactive FedCM sign-in the instant the screen mounts — would
|
|
316
|
+
// read `storage === null` and SILENTLY skip writing the session, leaving the
|
|
317
|
+
// user signed-in in-memory but with nothing to restore on reload.
|
|
318
|
+
//
|
|
319
|
+
// To make persistence robust regardless of timing we ALSO expose the storage
|
|
320
|
+
// as an awaitable promise (`getReadyStorage`). Persistence code awaits the
|
|
321
|
+
// ready instance instead of branching on the possibly-null state, so a write
|
|
322
|
+
// is never silently dropped just because it raced storage init.
|
|
202
323
|
const storageRef = useRef<StorageInterface | null>(null);
|
|
203
324
|
const [storage, setStorage] = useState<StorageInterface | null>(null);
|
|
204
325
|
|
|
326
|
+
// A single, stable deferred that resolves with the initialized storage. Built
|
|
327
|
+
// lazily via a ref initializer so the resolver is captured exactly once and
|
|
328
|
+
// the promise identity is stable across renders.
|
|
329
|
+
const buildStorageDeferred = () => {
|
|
330
|
+
let resolve: (storage: StorageInterface) => void = () => undefined;
|
|
331
|
+
const promise = new Promise<StorageInterface>((res) => {
|
|
332
|
+
resolve = res;
|
|
333
|
+
});
|
|
334
|
+
return { promise, resolve };
|
|
335
|
+
};
|
|
336
|
+
const storageReadyRef = useRef<ReturnType<typeof buildStorageDeferred> | null>(null);
|
|
337
|
+
if (storageReadyRef.current === null) {
|
|
338
|
+
storageReadyRef.current = buildStorageDeferred();
|
|
339
|
+
}
|
|
340
|
+
const storageReady = storageReadyRef.current;
|
|
341
|
+
|
|
342
|
+
// Resolve the storage instance that is guaranteed to be ready. Returns the
|
|
343
|
+
// already-initialized instance synchronously when available, otherwise awaits
|
|
344
|
+
// the init promise. Never resolves to `null`.
|
|
345
|
+
const getReadyStorage = useCallback((): Promise<StorageInterface> => {
|
|
346
|
+
if (storageRef.current) {
|
|
347
|
+
return Promise.resolve(storageRef.current);
|
|
348
|
+
}
|
|
349
|
+
return storageReady.promise;
|
|
350
|
+
}, [storageReady]);
|
|
351
|
+
|
|
205
352
|
useEffect(() => {
|
|
206
353
|
let mounted = true;
|
|
207
354
|
createPlatformStorage()
|
|
208
355
|
.then((storageInstance) => {
|
|
356
|
+
// Resolve the ready-promise even if the component unmounted: in-flight
|
|
357
|
+
// persistence awaiting it must still complete against a real store.
|
|
358
|
+
storageRef.current = storageInstance;
|
|
359
|
+
storageReady.resolve(storageInstance);
|
|
209
360
|
if (mounted) {
|
|
210
|
-
storageRef.current = storageInstance;
|
|
211
361
|
setStorage(storageInstance);
|
|
212
362
|
}
|
|
213
363
|
})
|
|
@@ -225,7 +375,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
225
375
|
return () => {
|
|
226
376
|
mounted = false;
|
|
227
377
|
};
|
|
228
|
-
}, [logger, onError]);
|
|
378
|
+
}, [logger, onError, storageReady]);
|
|
229
379
|
|
|
230
380
|
|
|
231
381
|
// Offline queuing is now handled by TanStack Query mutations
|
|
@@ -338,6 +488,148 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
338
488
|
const clearSessionStateRef = useRef(clearSessionState);
|
|
339
489
|
clearSessionStateRef.current = clearSessionState;
|
|
340
490
|
|
|
491
|
+
// Durable, navigation-safe session persistence.
|
|
492
|
+
//
|
|
493
|
+
// Writes the active-session id and appends the session id to the durable
|
|
494
|
+
// `session_ids` list, awaiting the READY storage instance (never the possibly
|
|
495
|
+
// -null `storage` state) so a write is never dropped because it raced storage
|
|
496
|
+
// init. Callers MUST invoke this BEFORE any work that can trigger a route
|
|
497
|
+
// navigation (`onAuthStateChange`) — navigation can interrupt a still-pending
|
|
498
|
+
// async write, which is exactly what once left `session_ids` empty after a
|
|
499
|
+
// successful sign-in. Shared by the FedCM/popup path and the cold-boot
|
|
500
|
+
// refresh-cookie restore so both land the same durable record.
|
|
501
|
+
const persistSessionDurably = useCallback(async (sessionId: string): Promise<void> => {
|
|
502
|
+
const readyStorage = await getReadyStorage();
|
|
503
|
+
await readyStorage.setItem(storageKeys.activeSessionId, sessionId);
|
|
504
|
+
const existingIds = await readyStorage.getItem(storageKeys.sessionIds);
|
|
505
|
+
let sessionIds: string[] = [];
|
|
506
|
+
try { sessionIds = existingIds ? JSON.parse(existingIds) : []; } catch { /* corrupted storage */ }
|
|
507
|
+
if (!sessionIds.includes(sessionId)) {
|
|
508
|
+
sessionIds.push(sessionId);
|
|
509
|
+
await readyStorage.setItem(storageKeys.sessionIds, JSON.stringify(sessionIds));
|
|
510
|
+
}
|
|
511
|
+
}, [getReadyStorage, storageKeys.activeSessionId, storageKeys.sessionIds]);
|
|
512
|
+
|
|
513
|
+
// Refs so the cold-boot restore can plant session state without widening its
|
|
514
|
+
// dependency array (mirrors the existing ref pattern above).
|
|
515
|
+
const setActiveSessionIdRef = useRef(setActiveSessionId);
|
|
516
|
+
setActiveSessionIdRef.current = setActiveSessionId;
|
|
517
|
+
const loginSuccessRef = useRef(loginSuccess);
|
|
518
|
+
loginSuccessRef.current = loginSuccess;
|
|
519
|
+
const onAuthStateChangeRef = useRef(onAuthStateChange);
|
|
520
|
+
onAuthStateChangeRef.current = onAuthStateChange;
|
|
521
|
+
|
|
522
|
+
// Cold-boot session restore via the secure refresh cookie (web only).
|
|
523
|
+
//
|
|
524
|
+
// On a hard reload the in-app, bearer-protected token fetch
|
|
525
|
+
// (`getTokenBySession` → `/session/token/:id`) 401s because there is no token
|
|
526
|
+
// in memory yet, which previously cleared the session and bounced the user to
|
|
527
|
+
// sign-in. Instead we call `POST {apiBaseUrl}/auth/refresh` with
|
|
528
|
+
// `credentials: 'include'` and NO Authorization header: the browser
|
|
529
|
+
// automatically attaches the first-party httpOnly `oxy_rt` cookie (set at
|
|
530
|
+
// login/signup/fedcm-exchange), the server validates + rotates it and returns
|
|
531
|
+
// a fresh session access token. JS never sees the refresh cookie (httpOnly) —
|
|
532
|
+
// that is the security property.
|
|
533
|
+
//
|
|
534
|
+
// Returns `true` when the session was restored (caller short-circuits the
|
|
535
|
+
// bearer path); `false` on 401 / no durable cookie / any failure (caller
|
|
536
|
+
// proceeds unauthenticated through the existing flow — nothing is cleared).
|
|
537
|
+
const restoreViaRefreshCookie = useCallback(async (): Promise<boolean> => {
|
|
538
|
+
if (!isWebBrowser()) {
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const apiBaseUrl = oxyServices.getBaseURL();
|
|
543
|
+
if (!apiBaseUrl) {
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
let response: Response;
|
|
548
|
+
try {
|
|
549
|
+
// Direct credentialed, no-auth POST. We deliberately bypass the SDK's
|
|
550
|
+
// HttpService here: it would attach the (absent) bearer and does not send
|
|
551
|
+
// cookies. The refresh cookie is scoped to `Path=/auth/refresh` and sent
|
|
552
|
+
// automatically same-site.
|
|
553
|
+
response = await fetch(`${apiBaseUrl.replace(/\/$/, '')}/auth/refresh`, {
|
|
554
|
+
method: 'POST',
|
|
555
|
+
credentials: 'include',
|
|
556
|
+
headers: { Accept: 'application/json' },
|
|
557
|
+
});
|
|
558
|
+
} catch (fetchError) {
|
|
559
|
+
// Offline / network error — fall through to the cached/stored-session flow.
|
|
560
|
+
if (__DEV__) {
|
|
561
|
+
loggerUtil.debug('Refresh-cookie restore network error (expected when offline)', { component: 'OxyContext', method: 'restoreViaRefreshCookie' }, fetchError as unknown);
|
|
562
|
+
}
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// 401 (no/expired/reused cookie) and any non-2xx → no durable session.
|
|
567
|
+
if (!response.ok) {
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
let payload: RefreshCookieResponse;
|
|
572
|
+
try {
|
|
573
|
+
payload = (await response.json()) as RefreshCookieResponse;
|
|
574
|
+
} catch {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const accessToken = payload.accessToken;
|
|
579
|
+
if (!accessToken) {
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Recover the session id from the (server-signed) access-token claims, or
|
|
584
|
+
// from the response body if the server included it. Decode-only; the server
|
|
585
|
+
// already verified the signature.
|
|
586
|
+
const claims = decodeAccessTokenClaims(accessToken);
|
|
587
|
+
const sessionId = payload.sessionId ?? claims?.sessionId;
|
|
588
|
+
if (!sessionId) {
|
|
589
|
+
// A token with no resolvable session id cannot drive multi-session state.
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Plant the fresh access token. The refresh token stays in the httpOnly
|
|
594
|
+
// cookie and is never touched by JS.
|
|
595
|
+
oxyServices.httpService.setTokens(accessToken);
|
|
596
|
+
|
|
597
|
+
// Fetch the full user with the freshly planted token.
|
|
598
|
+
let fullUser: User;
|
|
599
|
+
try {
|
|
600
|
+
fullUser = await oxyServices.getCurrentUser();
|
|
601
|
+
} catch (userError) {
|
|
602
|
+
// Token planted but profile fetch failed (e.g. transient network). Do not
|
|
603
|
+
// claim a restored session; fall through so the stored-session flow can
|
|
604
|
+
// retry. Leave the planted token in place — it is valid and harmless.
|
|
605
|
+
if (__DEV__) {
|
|
606
|
+
loggerUtil.debug('Refresh-cookie restore: getCurrentUser failed', { component: 'OxyContext', method: 'restoreViaRefreshCookie' }, userError as unknown);
|
|
607
|
+
}
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const userId = fullUser.id?.toString() ?? claims?.userId ?? '';
|
|
612
|
+
const now = new Date();
|
|
613
|
+
const clientSession: ClientSession = {
|
|
614
|
+
sessionId,
|
|
615
|
+
deviceId: '',
|
|
616
|
+
expiresAt: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
617
|
+
lastActive: now.toISOString(),
|
|
618
|
+
userId,
|
|
619
|
+
isCurrent: true,
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
// Restore the active session into the multi-session store (merge: keep any
|
|
623
|
+
// other sessions intact) and durably persist BEFORE notifying listeners.
|
|
624
|
+
updateSessionsRef.current([clientSession], { merge: true });
|
|
625
|
+
setActiveSessionIdRef.current(sessionId);
|
|
626
|
+
await persistSessionDurably(sessionId);
|
|
627
|
+
|
|
628
|
+
loginSuccessRef.current(fullUser);
|
|
629
|
+
onAuthStateChangeRef.current?.(fullUser);
|
|
630
|
+
return true;
|
|
631
|
+
}, [oxyServices, persistSessionDurably]);
|
|
632
|
+
|
|
341
633
|
const restoreSessionsFromStorage = useCallback(async (): Promise<void> => {
|
|
342
634
|
if (!storage) {
|
|
343
635
|
return;
|
|
@@ -346,6 +638,15 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
346
638
|
setTokenReady(false);
|
|
347
639
|
|
|
348
640
|
try {
|
|
641
|
+
// Web cold-boot fast path: restore the active session from the secure
|
|
642
|
+
// httpOnly refresh cookie before the bearer-protected stored-session
|
|
643
|
+
// validation (which 401s on a hard reload). On success we are signed in
|
|
644
|
+
// from the cookie alone — no FedCM needed. On failure we fall through to
|
|
645
|
+
// the existing stored-session flow below; nothing is cleared.
|
|
646
|
+
if (await restoreViaRefreshCookie()) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
349
650
|
const storedSessionIdsJson = await storage.getItem(storageKeys.sessionIds);
|
|
350
651
|
const storedSessionIds: string[] = storedSessionIdsJson ? JSON.parse(storedSessionIdsJson) : [];
|
|
351
652
|
const storedActiveSessionId = await storage.getItem(storageKeys.activeSessionId);
|
|
@@ -436,6 +737,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
436
737
|
storage,
|
|
437
738
|
storageKeys.activeSessionId,
|
|
438
739
|
storageKeys.sessionIds,
|
|
740
|
+
restoreViaRefreshCookie,
|
|
439
741
|
]);
|
|
440
742
|
|
|
441
743
|
useEffect(() => {
|
|
@@ -480,8 +782,25 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
480
782
|
updateSessions([clientSession], { merge: true });
|
|
481
783
|
setActiveSessionId(session.sessionId);
|
|
482
784
|
|
|
483
|
-
//
|
|
484
|
-
//
|
|
785
|
+
// Persist to storage BEFORE fetching the profile and BEFORE notifying the
|
|
786
|
+
// auth-state-change callback. The token is planted and the in-memory store
|
|
787
|
+
// is updated above; durably committing the session id here — ahead of
|
|
788
|
+
// `getCurrentUser()` / `loginSuccess` / `onAuthStateChange` — is critical
|
|
789
|
+
// because `onAuthStateChange` triggers a route navigation that can
|
|
790
|
+
// interrupt/supersede a still-pending async write. Persisting first
|
|
791
|
+
// guarantees the durable record lands; that is exactly what was missing
|
|
792
|
+
// when `oxy_session_session_ids` came back empty after a successful FedCM
|
|
793
|
+
// sign-in (the user appeared logged in until reload, then had no session to
|
|
794
|
+
// restore). `persistSessionDurably` awaits the READY storage instance rather
|
|
795
|
+
// than reading the possibly-null `storage` state, so a sign-in fired the
|
|
796
|
+
// instant the screen mounts (before storage-init populates state) is not
|
|
797
|
+
// silently dropped.
|
|
798
|
+
await persistSessionDurably(session.sessionId);
|
|
799
|
+
|
|
800
|
+
// Fetch the full user profile now that we have a valid access token and the
|
|
801
|
+
// session is durably persisted. The session only carries MinimalUserData;
|
|
802
|
+
// the store and callbacks expect a full User. The navigation kicked off by
|
|
803
|
+
// `onAuthStateChange` now happens only after the durable write is committed.
|
|
485
804
|
let fullUser: User;
|
|
486
805
|
try {
|
|
487
806
|
fullUser = await oxyServices.getCurrentUser();
|
|
@@ -492,19 +811,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
492
811
|
}
|
|
493
812
|
loginSuccess(fullUser);
|
|
494
813
|
onAuthStateChange?.(fullUser);
|
|
495
|
-
|
|
496
|
-
// Persist to storage
|
|
497
|
-
if (storage) {
|
|
498
|
-
await storage.setItem(storageKeys.activeSessionId, session.sessionId);
|
|
499
|
-
const existingIds = await storage.getItem(storageKeys.sessionIds);
|
|
500
|
-
let sessionIds: string[] = [];
|
|
501
|
-
try { sessionIds = existingIds ? JSON.parse(existingIds) : []; } catch { /* corrupted storage */ }
|
|
502
|
-
if (!sessionIds.includes(session.sessionId)) {
|
|
503
|
-
sessionIds.push(session.sessionId);
|
|
504
|
-
await storage.setItem(storageKeys.sessionIds, JSON.stringify(sessionIds));
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}, [oxyServices, updateSessions, setActiveSessionId, loginSuccess, onAuthStateChange, storage, storageKeys]);
|
|
814
|
+
}, [oxyServices, updateSessions, setActiveSessionId, loginSuccess, onAuthStateChange, persistSessionDurably]);
|
|
508
815
|
|
|
509
816
|
// Enable web SSO only after local storage check completes and no user found
|
|
510
817
|
const shouldTryWebSSO = isWebBrowser() && tokenReady && !user && initialized;
|
|
@@ -166,7 +166,15 @@ export const useSessionManagement = ({
|
|
|
166
166
|
setSessions([]);
|
|
167
167
|
setActiveSessionId(null);
|
|
168
168
|
logoutStore();
|
|
169
|
-
|
|
169
|
+
|
|
170
|
+
// Clear the access token on the client instance. Without this the
|
|
171
|
+
// TokenStore retained the stale bearer until the next 401, leaving the
|
|
172
|
+
// instance "logged in" at the HTTP layer and — via OxyProvider's
|
|
173
|
+
// token-change mirror — leaking that stale token onto the shared
|
|
174
|
+
// `oxyClient` singleton after sign-out. Clearing here fires
|
|
175
|
+
// `onTokensChanged(null)`, propagating the logged-out state everywhere.
|
|
176
|
+
oxyServices.clearTokens();
|
|
177
|
+
|
|
170
178
|
// Clear TanStack Query cache (in-memory)
|
|
171
179
|
if (queryClient) {
|
|
172
180
|
queryClient.clear();
|
|
@@ -185,7 +193,7 @@ export const useSessionManagement = ({
|
|
|
185
193
|
|
|
186
194
|
await clearSessionStorage();
|
|
187
195
|
onAuthStateChange?.(null);
|
|
188
|
-
}, [clearSessionStorage, logoutStore, onAuthStateChange, queryClient, storage, logger]);
|
|
196
|
+
}, [clearSessionStorage, logoutStore, onAuthStateChange, oxyServices, queryClient, storage, logger]);
|
|
189
197
|
|
|
190
198
|
const activateSession = useCallback(
|
|
191
199
|
async (sessionId: string, user: User): Promise<void> => {
|