@unchainedshop/api 4.6.1 → 4.7.2

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.
Files changed (75) hide show
  1. package/lib/acl.js +29 -1
  2. package/lib/api-index.d.ts +1 -0
  3. package/lib/auth.d.ts +44 -0
  4. package/lib/auth.js +187 -0
  5. package/lib/context.d.ts +1 -0
  6. package/lib/context.js +8 -3
  7. package/lib/events.d.ts +2 -0
  8. package/lib/events.js +2 -0
  9. package/lib/express/index.d.ts +6 -9
  10. package/lib/express/index.js +52 -125
  11. package/lib/express/mountPluginRoutes.d.ts +3 -0
  12. package/lib/express/mountPluginRoutes.js +59 -0
  13. package/lib/express/mountRoutes.d.ts +3 -0
  14. package/lib/express/mountRoutes.js +57 -0
  15. package/lib/fastify/index.d.ts +6 -9
  16. package/lib/fastify/index.js +51 -113
  17. package/lib/fastify/mountPluginRoutes.d.ts +3 -0
  18. package/lib/fastify/mountPluginRoutes.js +62 -0
  19. package/lib/fastify/mountRoutes.d.ts +3 -0
  20. package/lib/fastify/mountRoutes.js +60 -0
  21. package/lib/handlers/createBackchannelLogoutHandler.d.ts +4 -0
  22. package/lib/handlers/createBackchannelLogoutHandler.js +170 -0
  23. package/lib/mcp/tools/localization/getLocalizationsConfig.d.ts +1 -1
  24. package/lib/mcp/tools/localization/schemas.d.ts +34 -0
  25. package/lib/mcp/tools/order/handlers/getTopCustomers.d.ts +2 -0
  26. package/lib/mcp/tools/quotation/handlers/getQuotation.d.ts +2 -0
  27. package/lib/mcp/tools/quotation/handlers/listQuotations.d.ts +2 -0
  28. package/lib/mcp/tools/quotation/handlers/makeQuotationProposal.d.ts +2 -0
  29. package/lib/mcp/tools/quotation/handlers/rejectQuotation.d.ts +2 -0
  30. package/lib/mcp/tools/quotation/handlers/verifyQuotation.d.ts +2 -0
  31. package/lib/mcp/tools/system/handlers/countEvents.d.ts +8 -1
  32. package/lib/mcp/tools/system/handlers/countWork.d.ts +10 -1
  33. package/lib/mcp/tools/system/handlers/countWork.js +1 -0
  34. package/lib/mcp/tools/system/handlers/index.d.ts +4 -4
  35. package/lib/mcp/tools/system/handlers/listEvents.d.ts +12 -1
  36. package/lib/mcp/tools/system/handlers/listWork.d.ts +14 -1
  37. package/lib/mcp/tools/system/handlers/listWork.js +1 -0
  38. package/lib/mcp/tools/users/handlers/addUserEmail.d.ts +2 -0
  39. package/lib/mcp/tools/users/handlers/createUser.d.ts +2 -0
  40. package/lib/mcp/tools/users/handlers/enrollUser.d.ts +2 -0
  41. package/lib/mcp/tools/users/handlers/getCurrentUser.d.ts +2 -0
  42. package/lib/mcp/tools/users/handlers/getUser.d.ts +2 -0
  43. package/lib/mcp/tools/users/handlers/listUsers.d.ts +2 -0
  44. package/lib/mcp/tools/users/handlers/setUserTags.d.ts +2 -0
  45. package/lib/mcp/tools/users/handlers/setUserUsername.d.ts +2 -0
  46. package/lib/mcp/tools/users/handlers/updateUser.d.ts +2 -0
  47. package/lib/mcp/utils/getNormalizedQuotationDetails.d.ts +2 -0
  48. package/lib/mcp/utils/getNormalizedUserDetails.d.ts +2 -0
  49. package/lib/mcp/utils/normalizeMediaUrl.js +1 -1
  50. package/lib/mcp/utils/sanitizeLocalizationEntityData.d.ts +1 -1
  51. package/lib/mcp/utils/validateIsoCode.d.ts +1 -1
  52. package/lib/middleware/createAuthMiddleware.d.ts +29 -0
  53. package/lib/middleware/createAuthMiddleware.js +128 -0
  54. package/lib/resolvers/mutations/accounts/logoutAllSessions.d.ts +4 -0
  55. package/lib/resolvers/mutations/accounts/logoutAllSessions.js +11 -0
  56. package/lib/resolvers/mutations/index.d.ts +1 -6
  57. package/lib/resolvers/mutations/index.js +2 -12
  58. package/lib/resolvers/mutations/orders/updateCart.js +7 -1
  59. package/lib/resolvers/type/index.d.ts +0 -1
  60. package/lib/resolvers/type/order/order-delivery-pickup-types.d.ts +0 -1
  61. package/lib/resolvers/type/order/order-delivery-pickup-types.js +0 -7
  62. package/lib/resolvers/type/order/order-payment-base.d.ts +7 -0
  63. package/lib/resolvers/type/order/order-payment-base.js +14 -0
  64. package/lib/roles/index.js +1 -0
  65. package/lib/roles/loggedIn.js +1 -0
  66. package/lib/schema/mutation.js +6 -44
  67. package/lib/schema/types/common.d.ts +2 -0
  68. package/lib/schema/types/common.js +12 -0
  69. package/lib/schema/types/index.js +2 -2
  70. package/lib/schema/types/order/delivery.js +0 -2
  71. package/lib/utils/mapServiceError.d.ts +1 -0
  72. package/lib/utils/mapServiceError.js +59 -0
  73. package/lib/utils/maskError.d.ts +1 -0
  74. package/lib/utils/maskError.js +22 -0
  75. package/package.json +8 -17
@@ -22,6 +22,8 @@ export default function getTopCustomers(context: Context, params: Params<'TOP_CU
22
22
  services: any;
23
23
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
24
24
  username?: string | undefined;
25
+ tokenVersion?: number | undefined;
26
+ oidcLogoutAt?: Date | undefined;
25
27
  _id: string;
26
28
  } | null;
27
29
  currencyCode: string | null;
@@ -25,6 +25,8 @@ export default function getQuotation(context: Context, params: Params<'GET'>): P
25
25
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
26
26
  username?: string;
27
27
  meta?: any;
28
+ tokenVersion?: number;
29
+ oidcLogoutAt?: Date;
28
30
  created: Date;
29
31
  updated?: Date;
30
32
  };
@@ -26,6 +26,8 @@ export default function listQuotations(context: Context, params: Params<'LIST'>)
26
26
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
27
27
  username?: string;
28
28
  meta?: any;
29
+ tokenVersion?: number;
30
+ oidcLogoutAt?: Date;
29
31
  created: Date;
30
32
  updated?: Date;
31
33
  };
@@ -26,6 +26,8 @@ export default function makeQuotationProposal(context: Context, params: Params<'
26
26
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
27
27
  username?: string;
28
28
  meta?: any;
29
+ tokenVersion?: number;
30
+ oidcLogoutAt?: Date;
29
31
  created: Date;
30
32
  updated?: Date;
31
33
  };
@@ -26,6 +26,8 @@ export default function rejectQuotation(context: Context, params: Params<'REJECT
26
26
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
27
27
  username?: string;
28
28
  meta?: any;
29
+ tokenVersion?: number;
30
+ oidcLogoutAt?: Date;
29
31
  created: Date;
30
32
  updated?: Date;
31
33
  };
@@ -26,6 +26,8 @@ export default function verifyQuotation(context: Context, params: Params<'VERIFY
26
26
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
27
27
  username?: string;
28
28
  meta?: any;
29
+ tokenVersion?: number;
30
+ oidcLogoutAt?: Date;
29
31
  created: Date;
30
32
  updated?: Date;
31
33
  };
@@ -1,5 +1,12 @@
1
1
  import type { Context } from '../../../../context.ts';
2
- import type { EventCountOptions } from '../types.ts';
2
+ export interface EventCountOptions {
3
+ queryString?: string;
4
+ created?: {
5
+ start?: Date;
6
+ end?: Date;
7
+ };
8
+ types?: string[];
9
+ }
3
10
  declare const countEvents: ({ modules }: Context, options?: EventCountOptions) => Promise<{
4
11
  count: number;
5
12
  }>;
@@ -1,5 +1,14 @@
1
+ import { type WorkStatus } from '@unchainedshop/core-worker';
1
2
  import type { Context } from '../../../../context.ts';
2
- import type { WorkCountOptions } from '../types.ts';
3
+ export interface WorkCountOptions {
4
+ queryString?: string;
5
+ status?: WorkStatus[];
6
+ created?: {
7
+ start?: Date;
8
+ end?: Date;
9
+ };
10
+ types?: string[];
11
+ }
3
12
  declare const countWork: ({ modules }: Context, options?: WorkCountOptions) => Promise<{
4
13
  count: number;
5
14
  }>;
@@ -1,3 +1,4 @@
1
+ import {} from '@unchainedshop/core-worker';
1
2
  const countWork = async ({ modules }, options) => {
2
3
  const countOptions = options || {};
3
4
  const count = await modules.worker.count({
@@ -22,10 +22,10 @@ declare const _default: {
22
22
  WORKER_GET: (context: import("../../../../context.ts").Context, { workId }: import("../schemas.ts").Params<"WORKER_GET">) => Promise<{
23
23
  work: any;
24
24
  }>;
25
- WORKER_LIST: (context: import("@unchainedshop/core").UnchainedCore, options?: import("../types.ts").WorkListOptions) => Promise<{
25
+ WORKER_LIST: (context: import("@unchainedshop/core").UnchainedCore, options?: import("./listWork.ts").WorkListOptions) => Promise<{
26
26
  works: any[];
27
27
  }>;
28
- WORKER_COUNT: ({ modules }: import("../../../../context.ts").Context, options?: import("../types.ts").WorkCountOptions) => Promise<{
28
+ WORKER_COUNT: ({ modules }: import("../../../../context.ts").Context, options?: import("./countWork.ts").WorkCountOptions) => Promise<{
29
29
  count: number;
30
30
  }>;
31
31
  WORKER_FINISH_WORK: (context: import("../../../../context.ts").Context, options: import("../schemas.ts").Params<"WORKER_FINISH_WORK">) => Promise<{
@@ -47,10 +47,10 @@ declare const _default: {
47
47
  EVENT_GET: ({ modules }: import("../../../../context.ts").Context, { eventId }: import("../schemas.ts").Params<"EVENT_GET">) => Promise<{
48
48
  event: import("@unchainedshop/core-events").Event | null;
49
49
  }>;
50
- EVENT_LIST: ({ modules }: import("../../../../context.ts").Context, options?: import("../types.ts").EventListOptions) => Promise<{
50
+ EVENT_LIST: ({ modules }: import("../../../../context.ts").Context, options?: import("./listEvents.ts").EventListOptions) => Promise<{
51
51
  events: import("@unchainedshop/core-events").Event[];
52
52
  }>;
53
- EVENT_COUNT: ({ modules }: import("../../../../context.ts").Context, options?: import("../types.ts").EventCountOptions) => Promise<{
53
+ EVENT_COUNT: ({ modules }: import("../../../../context.ts").Context, options?: import("./countEvents.ts").EventCountOptions) => Promise<{
54
54
  count: number;
55
55
  }>;
56
56
  EVENT_STATISTICS: ({ modules }: import("../../../../context.ts").Context, options: import("../schemas.ts").Params<"EVENT_STATISTICS">) => Promise<{
@@ -1,5 +1,16 @@
1
+ import type { SortOption } from '@unchainedshop/utils';
1
2
  import type { Context } from '../../../../context.ts';
2
- import type { EventListOptions } from '../types.ts';
3
+ export interface EventListOptions {
4
+ limit?: number;
5
+ offset?: number;
6
+ queryString?: string;
7
+ types?: string[];
8
+ sort?: SortOption[];
9
+ created?: {
10
+ start?: Date;
11
+ end?: Date;
12
+ };
13
+ }
3
14
  declare const listEvents: ({ modules }: Context, options?: EventListOptions) => Promise<{
4
15
  events: import("@unchainedshop/core-events").Event[];
5
16
  }>;
@@ -1,5 +1,18 @@
1
- import type { WorkListOptions } from '../types.ts';
1
+ import { type SortOption } from '@unchainedshop/utils';
2
+ import { type WorkStatus } from '@unchainedshop/core-worker';
2
3
  import type { UnchainedCore } from '@unchainedshop/core';
4
+ export interface WorkListOptions {
5
+ limit?: number;
6
+ offset?: number;
7
+ queryString?: string;
8
+ status: WorkStatus[];
9
+ types?: string[];
10
+ sort?: SortOption[];
11
+ created?: {
12
+ start?: Date;
13
+ end?: Date;
14
+ };
15
+ }
3
16
  declare const listWork: (context: UnchainedCore, options?: WorkListOptions) => Promise<{
4
17
  works: any[];
5
18
  }>;
@@ -1,4 +1,5 @@
1
1
  import { buildObfuscatedFieldsFilter } from '@unchainedshop/utils';
2
+ import {} from '@unchainedshop/core-worker';
2
3
  const listWork = async (context, options) => {
3
4
  const { modules } = context;
4
5
  const removePrivateFieldsFromWork = buildObfuscatedFieldsFilter(context.options.worker?.blacklistedVariables);
@@ -37,6 +37,8 @@ export default function addUserEmail(context: Context, params: Params<'ADD_EMAIL
37
37
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
38
38
  username?: string;
39
39
  meta?: any;
40
+ tokenVersion?: number;
41
+ oidcLogoutAt?: Date;
40
42
  created: Date;
41
43
  updated?: Date;
42
44
  } | null;
@@ -37,6 +37,8 @@ export default function createUser(context: Context, params: Params<'CREATE'>):
37
37
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
38
38
  username?: string;
39
39
  meta?: any;
40
+ tokenVersion?: number;
41
+ oidcLogoutAt?: Date;
40
42
  created: Date;
41
43
  updated?: Date;
42
44
  } | null;
@@ -37,6 +37,8 @@ export default function enrollUser(context: Context, params: Params<'ENROLL'>):
37
37
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
38
38
  username?: string;
39
39
  meta?: any;
40
+ tokenVersion?: number;
41
+ oidcLogoutAt?: Date;
40
42
  created: Date;
41
43
  updated?: Date;
42
44
  } | null;
@@ -36,6 +36,8 @@ export default function getCurrentUser(context: Context): Promise<{
36
36
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
37
37
  username?: string;
38
38
  meta?: any;
39
+ tokenVersion?: number;
40
+ oidcLogoutAt?: Date;
39
41
  created: Date;
40
42
  updated?: Date;
41
43
  } | null;
@@ -37,6 +37,8 @@ export default function getUser(context: Context, params: Params<'GET'>): Promis
37
37
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
38
38
  username?: string;
39
39
  meta?: any;
40
+ tokenVersion?: number;
41
+ oidcLogoutAt?: Date;
40
42
  created: Date;
41
43
  updated?: Date;
42
44
  } | null;
@@ -37,6 +37,8 @@ export default function listUsers(context: Context, params: Params<'LIST'>): Pro
37
37
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
38
38
  username?: string;
39
39
  meta?: any;
40
+ tokenVersion?: number;
41
+ oidcLogoutAt?: Date;
40
42
  created: Date;
41
43
  updated?: Date;
42
44
  } | null)[];
@@ -37,6 +37,8 @@ export default function setUserTags(context: Context, params: Params<'SET_TAGS'>
37
37
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
38
38
  username?: string;
39
39
  meta?: any;
40
+ tokenVersion?: number;
41
+ oidcLogoutAt?: Date;
40
42
  created: Date;
41
43
  updated?: Date;
42
44
  } | null;
@@ -37,6 +37,8 @@ export default function setUserUsername(context: Context, params: Params<'SET_US
37
37
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
38
38
  username?: string;
39
39
  meta?: any;
40
+ tokenVersion?: number;
41
+ oidcLogoutAt?: Date;
40
42
  created: Date;
41
43
  updated?: Date;
42
44
  } | null;
@@ -37,6 +37,8 @@ export default function updateUser(context: Context, params: Params<'UPDATE'>):
37
37
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
38
38
  username?: string;
39
39
  meta?: any;
40
+ tokenVersion?: number;
41
+ oidcLogoutAt?: Date;
40
42
  created: Date;
41
43
  updated?: Date;
42
44
  } | null;
@@ -24,6 +24,8 @@ export declare function getNormalizedQuotationDetails(quotationId: string, conte
24
24
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
25
25
  username?: string;
26
26
  meta?: any;
27
+ tokenVersion?: number;
28
+ oidcLogoutAt?: Date;
27
29
  created: Date;
28
30
  updated?: Date;
29
31
  };
@@ -35,6 +35,8 @@ export declare function getNormalizedUserDetails(userId: string, context: Contex
35
35
  pushSubscriptions: import("@unchainedshop/core-users").PushSubscriptionObject[];
36
36
  username?: string;
37
37
  meta?: any;
38
+ tokenVersion?: number;
39
+ oidcLogoutAt?: Date;
38
40
  created: Date;
39
41
  updated?: Date;
40
42
  } | null>;
@@ -1,4 +1,4 @@
1
- import { getFileAdapter } from '@unchainedshop/core-files';
1
+ import { getFileAdapter } from '@unchainedshop/core';
2
2
  const normalizeMediaUrl = async (medias = [], context) => {
3
3
  if (medias?.length) {
4
4
  const normalizedMedias = await Promise.all(medias
@@ -1,4 +1,4 @@
1
- import type { LocalizationType, LocalizationEntity, LocalizationUpdateEntity } from '../tools/localization/types.ts';
1
+ import type { LocalizationType, LocalizationEntity, LocalizationUpdateEntity } from '../tools/localization/schemas.ts';
2
2
  declare const sanitizeEntityData: (localizationType: LocalizationType, entity: LocalizationEntity | LocalizationUpdateEntity) => {
3
3
  isoCode: string;
4
4
  contractAddress?: string;
@@ -1,3 +1,3 @@
1
- import type { LocalizationType } from '../tools/localization/types.ts';
1
+ import type { LocalizationType } from '../tools/localization/schemas.ts';
2
2
  declare const validateIsoCode: (localizationType: LocalizationType, isoCode: string) => string;
3
3
  export default validateIsoCode;
@@ -0,0 +1,29 @@
1
+ import { type AuthConfig } from '../auth.ts';
2
+ import type { LoginFn, LogoutFn } from '../context.ts';
3
+ export interface AuthContextParams {
4
+ getHeader: (key: string) => string | undefined;
5
+ setHeader: (key: string, value: string) => void;
6
+ getCookie: (name: string) => string | undefined;
7
+ setCookie: (name: string, value: string, options: CookieOptions) => void;
8
+ clearCookie: (name: string, options: CookieOptions) => void;
9
+ remoteAddress?: string;
10
+ remotePort?: number;
11
+ }
12
+ export interface CookieOptions {
13
+ domain?: string;
14
+ path?: string;
15
+ secure?: boolean;
16
+ httpOnly?: boolean;
17
+ sameSite?: 'strict' | 'lax' | 'none' | boolean;
18
+ maxAge?: number;
19
+ expires?: Date;
20
+ }
21
+ export interface AuthContext {
22
+ userId?: string;
23
+ tokenVersion?: number;
24
+ impersonatorId?: string;
25
+ accessToken?: string;
26
+ login: LoginFn;
27
+ logout: LogoutFn;
28
+ }
29
+ export declare function createAuthContext(params: AuthContextParams, authConfig?: AuthConfig): Promise<AuthContext>;
@@ -0,0 +1,128 @@
1
+ import { emit } from '@unchainedshop/events';
2
+ import { signAccessToken, createAuthHandler, generateFingerprint, verifyFingerprint, } from "../auth.js";
3
+ import { API_EVENTS } from "../events.js";
4
+ import { createLogger } from '@unchainedshop/logger';
5
+ const logger = createLogger('unchained:api:auth-middleware');
6
+ const { UNCHAINED_COOKIE_NAME = 'unchained_token', UNCHAINED_COOKIE_PATH = '/', UNCHAINED_COOKIE_DOMAIN, UNCHAINED_COOKIE_SAMESITE = 'lax', UNCHAINED_COOKIE_INSECURE, UNCHAINED_FINGERPRINT_COOKIE_NAME = '__Secure-fgp', UNCHAINED_TOKEN_EXPIRY_SECONDS = '3600', } = process.env;
7
+ function getTokenCookieOptions(expires) {
8
+ const secure = !UNCHAINED_COOKIE_INSECURE;
9
+ const sameSite = {
10
+ none: 'none',
11
+ lax: 'lax',
12
+ strict: 'strict',
13
+ '1': true,
14
+ '0': false,
15
+ }[UNCHAINED_COOKIE_SAMESITE?.trim()?.toLowerCase()] || 'lax';
16
+ if (!secure && process.env.NODE_ENV === 'production') {
17
+ logger.warn('SECURITY WARNING: Running with UNCHAINED_COOKIE_INSECURE in production is not recommended');
18
+ }
19
+ if (sameSite === 'none' && !secure) {
20
+ logger.warn('SECURITY WARNING: SameSite=None requires Secure flag to be effective');
21
+ }
22
+ const expirySeconds = parseInt(UNCHAINED_TOKEN_EXPIRY_SECONDS, 10);
23
+ return {
24
+ domain: UNCHAINED_COOKIE_DOMAIN,
25
+ path: UNCHAINED_COOKIE_PATH,
26
+ secure,
27
+ httpOnly: true,
28
+ sameSite: sameSite,
29
+ maxAge: expires ? undefined : expirySeconds * 1000,
30
+ expires,
31
+ };
32
+ }
33
+ function getFingerprintCookieOptions(expires) {
34
+ const secure = !UNCHAINED_COOKIE_INSECURE;
35
+ const expirySeconds = parseInt(UNCHAINED_TOKEN_EXPIRY_SECONDS, 10);
36
+ return {
37
+ domain: UNCHAINED_COOKIE_DOMAIN,
38
+ path: UNCHAINED_COOKIE_PATH,
39
+ secure,
40
+ httpOnly: true,
41
+ sameSite: 'strict',
42
+ maxAge: expires ? undefined : expirySeconds * 1000,
43
+ expires,
44
+ };
45
+ }
46
+ function extractBearerToken(authHeader) {
47
+ if (!authHeader)
48
+ return undefined;
49
+ const parts = authHeader.split(' ');
50
+ if (parts.length !== 2)
51
+ return undefined;
52
+ const [scheme, token] = parts;
53
+ if (scheme.toLowerCase() !== 'bearer') {
54
+ logger.debug('Authorization header present but not Bearer scheme');
55
+ return undefined;
56
+ }
57
+ return token;
58
+ }
59
+ export async function createAuthContext(params, authConfig) {
60
+ const { getHeader, getCookie, setCookie, clearCookie } = params;
61
+ const authHeader = getHeader('authorization');
62
+ const headerToken = extractBearerToken(authHeader);
63
+ const cookieToken = getCookie(UNCHAINED_COOKIE_NAME);
64
+ const token = headerToken || cookieToken;
65
+ const fingerprintCookie = getCookie(UNCHAINED_FINGERPRINT_COOKIE_NAME);
66
+ const verifyToken = createAuthHandler(authConfig);
67
+ const authResult = token ? await verifyToken(token) : {};
68
+ let fingerprintValid = true;
69
+ if (authResult.fingerprintHash && fingerprintCookie) {
70
+ fingerprintValid = verifyFingerprint(fingerprintCookie, authResult.fingerprintHash);
71
+ if (!fingerprintValid) {
72
+ logger.warn('Token sidejacking detected: fingerprint mismatch', {
73
+ userId: authResult.userId,
74
+ });
75
+ authResult.userId = undefined;
76
+ authResult.tokenVersion = undefined;
77
+ authResult.impersonatorId = undefined;
78
+ }
79
+ }
80
+ else if (authResult.fingerprintHash && !fingerprintCookie) {
81
+ logger.warn('Token sidejacking detected: fingerprint cookie missing', {
82
+ userId: authResult.userId,
83
+ });
84
+ fingerprintValid = false;
85
+ authResult.userId = undefined;
86
+ authResult.tokenVersion = undefined;
87
+ authResult.impersonatorId = undefined;
88
+ }
89
+ const login = async (user, options = {}) => {
90
+ const { impersonator } = options;
91
+ const tokenVersion = user.tokenVersion ?? 1;
92
+ const { raw: fingerprintRaw, hash: fingerprintHash } = generateFingerprint();
93
+ const { token: newToken, expires } = await signAccessToken(user._id, tokenVersion, {
94
+ impersonatorId: impersonator?._id,
95
+ fingerprintHash,
96
+ });
97
+ setCookie(UNCHAINED_COOKIE_NAME, newToken, getTokenCookieOptions(expires));
98
+ setCookie(UNCHAINED_FINGERPRINT_COOKIE_NAME, fingerprintRaw, getFingerprintCookieOptions(expires));
99
+ const tokenObject = {
100
+ _id: crypto.randomUUID(),
101
+ userId: user._id,
102
+ tokenExpires: expires,
103
+ };
104
+ await emit(API_EVENTS.API_LOGIN_TOKEN_CREATED, tokenObject);
105
+ user._inLoginMethodResponse = true;
106
+ return { user, ...tokenObject };
107
+ };
108
+ const logout = async () => {
109
+ clearCookie(UNCHAINED_COOKIE_NAME, getTokenCookieOptions());
110
+ clearCookie(UNCHAINED_FINGERPRINT_COOKIE_NAME, getFingerprintCookieOptions());
111
+ if (authResult.userId) {
112
+ const tokenObject = {
113
+ _id: crypto.randomUUID(),
114
+ userId: authResult.userId,
115
+ };
116
+ await emit(API_EVENTS.API_LOGOUT, tokenObject);
117
+ }
118
+ return true;
119
+ };
120
+ return {
121
+ userId: authResult.userId,
122
+ tokenVersion: authResult.tokenVersion,
123
+ impersonatorId: authResult.impersonatorId,
124
+ accessToken: authResult.isApiKey ? authResult.accessToken : undefined,
125
+ login,
126
+ logout,
127
+ };
128
+ }
@@ -0,0 +1,4 @@
1
+ import type { Context } from '../../../context.ts';
2
+ export default function logoutAllSessions(root: never, _: never, context: Context): Promise<{
3
+ success: boolean;
4
+ }>;
@@ -0,0 +1,11 @@
1
+ import { log } from '@unchainedshop/logger';
2
+ export default async function logoutAllSessions(root, _, context) {
3
+ const { userId, modules } = context;
4
+ log('mutation logoutAllSessions', { userId });
5
+ const result = await modules.users.incrementTokenVersion(userId);
6
+ if (!result) {
7
+ throw new Error('Failed to logout all sessions', { cause: 'LOGOUT_FAILED' });
8
+ }
9
+ await context.logout();
10
+ return { success: true };
11
+ }
@@ -1,5 +1,6 @@
1
1
  declare const _default: {
2
2
  logout: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
3
+ logoutAllSessions: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
3
4
  loginAsGuest: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
4
5
  impersonate: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
5
6
  stopImpersonation: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
@@ -85,12 +86,6 @@ declare const _default: {
85
86
  updateCartDeliveryShipping: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
86
87
  updateCartPaymentGeneric: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
87
88
  updateCartPaymentInvoice: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
88
- setOrderPaymentProvider: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
89
- setOrderDeliveryProvider: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
90
- updateOrderDeliveryShipping: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
91
- updateOrderDeliveryPickUp: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
92
- updateOrderPaymentGeneric: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
93
- updateOrderPaymentInvoice: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
94
89
  removeOrder: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
95
90
  confirmOrder: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
96
91
  rejectOrder: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
@@ -4,6 +4,7 @@ import loginWithPassword from "./accounts/loginWithPassword.js";
4
4
  import loginWithWebAuthn from "./accounts/loginWithWebAuthn.js";
5
5
  import loginAsGuest from "./accounts/loginAsGuest.js";
6
6
  import logout from "./accounts/logout.js";
7
+ import logoutAllSessions from "./accounts/logoutAllSessions.js";
7
8
  import impersonate from "./accounts/impersonate.js";
8
9
  import stopImpersonation from "./accounts/stopImpersonation.js";
9
10
  import changePassword from "./accounts/changePassword.js";
@@ -69,8 +70,6 @@ import updateCartItem from "./orders/updateCartItem.js";
69
70
  import removeCartItem from "./orders/removeCartItem.js";
70
71
  import removeCartDiscount from "./orders/removeCartDiscount.js";
71
72
  import removeOrder from "./orders/removeOrder.js";
72
- import setOrderPaymentProvider from "./orders/setOrderPaymentProvider.js";
73
- import setOrderDeliveryProvider from "./orders/setOrderDeliveryProvider.js";
74
73
  import confirmOrder from "./orders/confirmOrder.js";
75
74
  import payOrder from "./orders/payOrder.js";
76
75
  import deliverOrder from "./orders/deliverOrder.js";
@@ -134,10 +133,6 @@ import updateEnrollment from "./enrollments/updateEnrollment.js";
134
133
  import registerPaymentCredentials from "./payment/registerPaymentCredentials.js";
135
134
  import markPaymentCredentialsPreferred from "./payment/markPaymentCredentialsPreferred.js";
136
135
  import removePaymentCredentials from "./payment/removePaymentCredentials.js";
137
- import updateOrderDeliveryShipping from "./orders/updateOrderDeliveryShipping.js";
138
- import updateOrderDeliveryPickUp from "./orders/updateOrderDeliveryPickUp.js";
139
- import updateOrderPaymentGeneric from "./orders/updateOrderPaymentGeneric.js";
140
- import updateOrderPaymentInvoice from "./orders/updateOrderPaymentInvoice.js";
141
136
  import signPaymentProviderForCredentialRegistration from "./payment/signPaymentProviderForCredentialRegistration.js";
142
137
  import signPaymentProviderForCheckout from "./orders/signPaymentProviderForCheckout.js";
143
138
  import pageView from "./pageView.js";
@@ -157,6 +152,7 @@ import updateCartPaymentGeneric from "./orders/updateCartPaymentGeneric.js";
157
152
  import updateCartPaymentInvoice from "./orders/updateCartPaymentInvoice.js";
158
153
  export default {
159
154
  logout: acl(actions.logout)(logout),
155
+ logoutAllSessions: acl(actions.logoutAllSessions)(logoutAllSessions),
160
156
  loginAsGuest: acl(actions.loginAsGuest)(loginAsGuest),
161
157
  impersonate: acl(actions.impersonate)(impersonate),
162
158
  stopImpersonation: acl(actions.stopImpersonation)(stopImpersonation),
@@ -242,12 +238,6 @@ export default {
242
238
  updateCartDeliveryShipping: acl(actions.updateCart)(updateCartDeliveryShipping),
243
239
  updateCartPaymentGeneric: acl(actions.updateCart)(updateCartPaymentGeneric),
244
240
  updateCartPaymentInvoice: acl(actions.updateCart)(updateCartPaymentInvoice),
245
- setOrderPaymentProvider: acl(actions.updateOrder)(setOrderPaymentProvider),
246
- setOrderDeliveryProvider: acl(actions.updateOrder)(setOrderDeliveryProvider),
247
- updateOrderDeliveryShipping: acl(actions.updateOrderDelivery)(updateOrderDeliveryShipping),
248
- updateOrderDeliveryPickUp: acl(actions.updateOrderDelivery)(updateOrderDeliveryPickUp),
249
- updateOrderPaymentGeneric: acl(actions.updateOrderPayment)(updateOrderPaymentGeneric),
250
- updateOrderPaymentInvoice: acl(actions.updateOrderPayment)(updateOrderPaymentInvoice),
251
241
  removeOrder: acl(actions.updateOrder)(removeOrder),
252
242
  confirmOrder: acl(actions.markOrderConfirmed)(confirmOrder),
253
243
  rejectOrder: acl(actions.markOrderRejected)(rejectOrder),
@@ -1,9 +1,15 @@
1
1
  import { log } from '@unchainedshop/logger';
2
- import { OrderNotFoundError, OrderWrongStatusError } from "../../../errors.js";
2
+ import { InvalidIdError, OrderNotFoundError, OrderWrongStatusError } from "../../../errors.js";
3
3
  export default async function updateCart(root, params, context) {
4
4
  const { orderId, billingAddress, contact, paymentProviderId, deliveryProviderId, meta } = params;
5
5
  const { modules, services, userId, user } = context;
6
6
  log('mutation updateCart', { userId });
7
+ if (orderId === '')
8
+ throw new InvalidIdError({ orderId });
9
+ if (paymentProviderId === '')
10
+ throw new InvalidIdError({ paymentProviderId });
11
+ if (deliveryProviderId === '')
12
+ throw new InvalidIdError({ deliveryProviderId });
7
13
  const order = await services.orders.findOrInitCart({
8
14
  orderId,
9
15
  user: user,
@@ -330,7 +330,6 @@ declare const types: {
330
330
  };
331
331
  OrderDeliveryPickUp: {
332
332
  activePickUpLocation(orderDelivery: import("@unchainedshop/core-orders").OrderDelivery, _: never, requestContext: import("../../context.ts").Context): Promise<import("@unchainedshop/core-delivery").DeliveryLocation | null>;
333
- pickUpLocations(obj: import("@unchainedshop/core-orders").OrderDelivery, _: never, context: import("../../context.ts").Context): Promise<import("@unchainedshop/core-delivery").DeliveryLocation[]>;
334
333
  provider(obj: import("@unchainedshop/core-orders").OrderDelivery, _: never, { loaders }: import("../../context.ts").Context): Promise<import("@unchainedshop/core-delivery").DeliveryProvider>;
335
334
  status(obj: import("@unchainedshop/core-orders").OrderDelivery, _: never, { modules }: import("../../context.ts").Context): import("@unchainedshop/core-orders").OrderDeliveryStatus;
336
335
  discounts(obj: import("@unchainedshop/core-orders").OrderDelivery, _: never, { loaders }: import("../../context.ts").Context): Promise<{
@@ -2,7 +2,6 @@ import type { Context } from '../../../context.ts';
2
2
  import type { OrderDelivery } from '@unchainedshop/core-orders';
3
3
  export declare const OrderDeliveryPickUp: {
4
4
  activePickUpLocation(orderDelivery: OrderDelivery, _: never, requestContext: Context): Promise<import("@unchainedshop/core-delivery").DeliveryLocation | null>;
5
- pickUpLocations(obj: OrderDelivery, _: never, context: Context): Promise<import("@unchainedshop/core-delivery").DeliveryLocation[]>;
6
5
  provider(obj: OrderDelivery, _: never, { loaders }: Context): Promise<import("@unchainedshop/core-delivery").DeliveryProvider>;
7
6
  status(obj: OrderDelivery, _: never, { modules }: Context): import("@unchainedshop/core-orders").OrderDeliveryStatus;
8
7
  discounts(obj: OrderDelivery, _: never, { loaders }: Context): Promise<{
@@ -8,13 +8,6 @@ export const OrderDeliveryPickUp = {
8
8
  const director = await DeliveryDirector.actions(provider, { orderDelivery: orderDelivery }, requestContext);
9
9
  return director.pickUpLocationById(orderPickUpLocationId);
10
10
  },
11
- async pickUpLocations(obj, _, context) {
12
- const provider = await context.loaders.deliveryProviderLoader.load({
13
- deliveryProviderId: obj.deliveryProviderId,
14
- });
15
- const director = await DeliveryDirector.actions(provider, { orderDelivery: obj }, context);
16
- return director.pickUpLocations();
17
- },
18
11
  async provider(obj, _, { loaders }) {
19
12
  return loaders.deliveryProviderLoader.load({
20
13
  deliveryProviderId: obj.deliveryProviderId,
@@ -0,0 +1,7 @@
1
+ import type { OrderPayment } from '@unchainedshop/core-orders';
2
+ import type { Context } from '../../../context.ts';
3
+ export declare const OrderPaymentBase: {
4
+ status(obj: OrderPayment, _: never, { modules }: Context): import("@unchainedshop/core-orders").OrderPaymentStatus;
5
+ provider(obj: OrderPayment, _: never, { loaders }: Context): Promise<import("@unchainedshop/core-payment").PaymentProvider>;
6
+ discounts(obj: OrderPayment, _: never, { loaders, services }: Context): Promise<import("@unchainedshop/core/lib/services/getPaymentDiscounts.js").PaymentDiscountPrice[]>;
7
+ };
@@ -0,0 +1,14 @@
1
+ export const OrderPaymentBase = {
2
+ status(obj, _, { modules }) {
3
+ return modules.orders.payments.normalizedStatus(obj);
4
+ },
5
+ async provider(obj, _, { loaders }) {
6
+ return loaders.paymentProviderLoader.load({
7
+ paymentProviderId: obj.paymentProviderId,
8
+ });
9
+ },
10
+ async discounts(obj, _, { loaders, services }) {
11
+ const order = await loaders.orderLoader.load({ orderId: obj.orderId });
12
+ return services.payment.getPaymentDiscounts(obj, order.currencyCode);
13
+ },
14
+ };