@commercetools/connect-payments-sdk 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/api/context/types/request-context.type.d.ts +2 -2
  3. package/dist/api/handlers/status.handler.d.ts +3 -5
  4. package/dist/api/handlers/status.handler.js +4 -7
  5. package/dist/api/hooks/session-auth.hook.d.ts +14 -13
  6. package/dist/api/hooks/session-auth.hook.js +18 -27
  7. package/dist/api/hooks/types/hook.type.d.ts +7 -0
  8. package/dist/api/index.d.ts +1 -0
  9. package/dist/api/index.js +1 -0
  10. package/dist/commercetools/index.d.ts +2 -0
  11. package/dist/commercetools/services/ct-authorization.service.d.ts +18 -0
  12. package/dist/{security/services/oauth2.service.js → commercetools/services/ct-authorization.service.js} +10 -21
  13. package/dist/commercetools/services/ct-payment.service.d.ts +6 -1
  14. package/dist/commercetools/services/ct-payment.service.js +72 -0
  15. package/dist/commercetools/services/ct-session.service.d.ts +16 -0
  16. package/dist/commercetools/services/ct-session.service.js +45 -0
  17. package/dist/commercetools/types/api.type.d.ts +11 -0
  18. package/dist/commercetools/types/authorization.type.d.ts +9 -0
  19. package/dist/commercetools/types/payment.type.d.ts +10 -0
  20. package/dist/commercetools/types/session.type.d.ts +28 -0
  21. package/dist/commercetools/types/session.type.js +2 -0
  22. package/dist/errorx/errorx.d.ts +3 -0
  23. package/dist/errorx/errorx.js +12 -1
  24. package/dist/index.d.ts +4 -8
  25. package/dist/index.js +16 -14
  26. package/dist/security/authn/authns.d.ts +24 -0
  27. package/dist/security/authn/authns.js +60 -0
  28. package/dist/security/authn/session-authn-manager.d.ts +10 -0
  29. package/dist/security/authn/session-authn-manager.js +25 -0
  30. package/dist/security/authn/types/authn.type.d.ts +18 -0
  31. package/dist/security/authn/types/authn.type.js +2 -0
  32. package/dist/security/index.d.ts +3 -3
  33. package/dist/security/index.js +3 -3
  34. package/package.json +2 -2
  35. package/.husky/pre-commit +0 -4
  36. package/dist/security/auth/session.auth.d.ts +0 -20
  37. package/dist/security/auth/session.auth.js +0 -54
  38. package/dist/security/services/oauth2.service.d.ts +0 -16
  39. package/dist/security/types/oauth2.type.d.ts +0 -13
  40. package/dist/security/types/session.type.d.ts +0 -10
  41. /package/dist/{security/types/oauth2.type.js → api/hooks/types/hook.type.js} +0 -0
  42. /package/dist/{security/types/session.type.js → commercetools/types/authorization.type.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @commercetools/connect-payments-sdk
2
2
 
3
+ ## 0.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - f3f238f: Authentication and authorization layer via session API
8
+
3
9
  ## 0.0.1
4
10
 
5
11
  ### Patch Changes
@@ -1,4 +1,4 @@
1
- import { PaymentSessionData } from '../../../security';
1
+ import { Authentication } from '../../../security';
2
2
  /**
3
3
  * Context provider interface
4
4
  */
@@ -12,5 +12,5 @@ export interface ContextProvider<T> {
12
12
  export type RequestContextData = {
13
13
  correlationId: string;
14
14
  requestId: string;
15
- sessionData?: PaymentSessionData;
15
+ authentication?: Authentication;
16
16
  };
@@ -1,4 +1,4 @@
1
- import { Oauth2Service } from '../../security/types/oauth2.type';
1
+ import { CommercetoolsAuthorizationService } from '../../commercetools';
2
2
  import { HandlerResponse } from './types/handler.type';
3
3
  export type HealthCheckResult = {
4
4
  name: string;
@@ -16,10 +16,8 @@ export declare const statusHandler: (options: {
16
16
  * @param opts
17
17
  * @returns
18
18
  */
19
- export declare const healthCheckCoCoPermissions: (opts: {
20
- oauth2Service: Oauth2Service;
21
- clientId: string;
22
- clientSecret: string;
19
+ export declare const healthCheckCommercetoolsPermissions: (opts: {
20
+ ctAuthorizationService: CommercetoolsAuthorizationService;
23
21
  projectKey: string;
24
22
  requiredPermissions: string[];
25
23
  }) => () => Promise<HealthCheckResult>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.healthCheckCoCoPermissions = exports.statusHandler = void 0;
3
+ exports.healthCheckCommercetoolsPermissions = exports.statusHandler = void 0;
4
4
  const statusHandler = (options) => async () => {
5
5
  const status = {
6
6
  timestamp: new Date().toISOString(),
@@ -45,11 +45,8 @@ exports.statusHandler = statusHandler;
45
45
  * @param opts
46
46
  * @returns
47
47
  */
48
- const healthCheckCoCoPermissions = (opts) => async () => {
49
- const token = await opts.oauth2Service.getAccessToken({
50
- clientId: opts.clientId,
51
- clientSecret: opts.clientSecret,
52
- });
48
+ const healthCheckCommercetoolsPermissions = (opts) => async () => {
49
+ const token = await opts.ctAuthorizationService.getAccessToken();
53
50
  const foundAll = opts.requiredPermissions.every((currentScope) => token.scope.split(' ').some((scopeInToken) => scopeInToken === `${currentScope}:${opts.projectKey}`));
54
51
  if (foundAll) {
55
52
  return {
@@ -70,4 +67,4 @@ const healthCheckCoCoPermissions = (opts) => async () => {
70
67
  },
71
68
  };
72
69
  };
73
- exports.healthCheckCoCoPermissions = healthCheckCoCoPermissions;
70
+ exports.healthCheckCommercetoolsPermissions = healthCheckCommercetoolsPermissions;
@@ -1,15 +1,16 @@
1
+ /// <reference types="node" />
1
2
  import { IncomingHttpHeaders } from 'node:http';
2
- import { SessionAuthenticator } from '../../security/types/session.type';
3
3
  import { ContextProvider, RequestContextData } from '../context/types/request-context.type';
4
- export type SessionAuthHookOptions = {
5
- sessionAuthenticator: SessionAuthenticator;
6
- contextProvider: ContextProvider<RequestContextData>;
7
- };
8
- /**
9
- * Fastify hook to authenticate and getting session information
10
- * @param opts
11
- * @returns
12
- */
13
- export declare const sessionAuthHook: (opts: SessionAuthHookOptions) => (req: {
14
- headers: IncomingHttpHeaders;
15
- }) => Promise<void>;
4
+ import { SessionAuthenticationManager } from '../../security';
5
+ import { AuthenticationHook } from './types/hook.type';
6
+ export declare class SessionAuthenticationHook implements AuthenticationHook {
7
+ private authenticationManager;
8
+ private contextProvider;
9
+ constructor(opts: {
10
+ authenticationManager: SessionAuthenticationManager;
11
+ contextProvider: ContextProvider<RequestContextData>;
12
+ });
13
+ authenticate(): (request: {
14
+ headers: IncomingHttpHeaders;
15
+ }) => Promise<void>;
16
+ }
@@ -1,31 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sessionAuthHook = void 0;
4
- const errorx_1 = require("../../errorx/errorx");
5
- /**
6
- * Fastify hook to authenticate and getting session information
7
- * @param opts
8
- * @returns
9
- */
10
- const sessionAuthHook = (opts) => async (req) => {
11
- const sessionId = req.headers['x-session-id'];
12
- if (sessionId) {
13
- const sessionData = await opts.sessionAuthenticator.introspectSession({
14
- sessionId,
15
- });
16
- opts.contextProvider.updateContextData({
17
- sessionData,
18
- });
19
- //updateSessionContext(sessionData);
3
+ exports.SessionAuthenticationHook = void 0;
4
+ const security_1 = require("../../security");
5
+ class SessionAuthenticationHook {
6
+ authenticationManager;
7
+ contextProvider;
8
+ constructor(opts) {
9
+ this.authenticationManager = opts.authenticationManager;
10
+ this.contextProvider = opts.contextProvider;
20
11
  }
21
- else {
22
- //TODO: throw a forbidden error
23
- throw new errorx_1.ErrorAuthErrorResponse({
24
- privateMessage: 'Session is not valid',
25
- fields: {
26
- id: sessionId,
27
- },
28
- });
12
+ authenticate() {
13
+ return async (request) => {
14
+ const sessionIdAuthn = new security_1.HeaderBasedAuthentication(request.headers['x-session-id']);
15
+ const authn = await this.authenticationManager.authenticate(sessionIdAuthn);
16
+ this.contextProvider.updateContextData({
17
+ authentication: authn,
18
+ });
19
+ };
29
20
  }
30
- };
31
- exports.sessionAuthHook = sessionAuthHook;
21
+ }
22
+ exports.SessionAuthenticationHook = SessionAuthenticationHook;
@@ -0,0 +1,7 @@
1
+ /// <reference types="node" />
2
+ import { IncomingHttpHeaders } from 'node:http';
3
+ export interface AuthenticationHook {
4
+ authenticate(): (request: {
5
+ headers: IncomingHttpHeaders;
6
+ }) => Promise<void>;
7
+ }
@@ -3,3 +3,4 @@ export * from './context/types/request-context.type';
3
3
  export * from './handlers/config.handler';
4
4
  export * from './handlers/status.handler';
5
5
  export * from './hooks/session-auth.hook';
6
+ export * from './hooks/types/hook.type';
package/dist/api/index.js CHANGED
@@ -19,3 +19,4 @@ __exportStar(require("./context/types/request-context.type"), exports);
19
19
  __exportStar(require("./handlers/config.handler"), exports);
20
20
  __exportStar(require("./handlers/status.handler"), exports);
21
21
  __exportStar(require("./hooks/session-auth.hook"), exports);
22
+ __exportStar(require("./hooks/types/hook.type"), exports);
@@ -1,2 +1,4 @@
1
1
  export { CartService as CommercetoolsCartService } from './types/cart.type';
2
2
  export { PaymentService as CommercetoolsPaymentService, TransactionData, UpdatePayment } from './types/payment.type';
3
+ export { SessionService as CommercetoolsSessionService, Session } from './types/session.type';
4
+ export { AuthorizationService as CommercetoolsAuthorizationService } from './types/authorization.type';
@@ -0,0 +1,18 @@
1
+ import { Fetch } from '../../fetch/types/fetch.type';
2
+ import { Logger } from '../../logger';
3
+ import { AuthorizationService, CommercetoolsToken } from '../types/authorization.type';
4
+ export declare class DefaultAuthorizationService implements AuthorizationService {
5
+ private authUrl;
6
+ private clientId;
7
+ private clientSecret;
8
+ private fetch;
9
+ private logger?;
10
+ constructor(opts: {
11
+ authUrl: string;
12
+ clientId: string;
13
+ clientSecret: string;
14
+ fetch: Fetch;
15
+ logger?: Logger;
16
+ });
17
+ getAccessToken(): Promise<CommercetoolsToken>;
18
+ }
@@ -1,32 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DefaultOauth2Service = void 0;
3
+ exports.DefaultAuthorizationService = void 0;
4
4
  const errorx_1 = require("../../errorx/errorx");
5
- class DefaultOauth2Service {
5
+ class DefaultAuthorizationService {
6
6
  authUrl;
7
+ clientId;
8
+ clientSecret;
7
9
  fetch;
8
10
  logger;
9
11
  constructor(opts) {
10
12
  this.authUrl = opts.authUrl;
13
+ this.clientId = opts.clientId;
14
+ this.clientSecret = opts.clientSecret;
11
15
  this.fetch = opts.fetch;
12
16
  this.logger = opts.logger;
13
17
  }
14
- oauth2tokenCache = new Map();
15
- oauth2tokenKey(clientId, clientSecret) {
16
- return `${clientId}:${clientSecret}`;
17
- }
18
- async getAccessToken(opts) {
19
- const token = this.oauth2tokenCache.get(this.oauth2tokenKey(opts.clientId, opts.clientSecret));
20
- // Check if token is valid for at least 1 hour
21
- if (token && token.expiresAt + 3600 * 1000 > Date.now()) {
22
- if (this.logger) {
23
- this.logger.debug({
24
- isRenewal: token ? true : false,
25
- }, 'Renewing token access token');
26
- }
27
- return token.token;
28
- }
29
- const encodedCredentials = btoa(`${opts.clientId}:${opts.clientSecret}`);
18
+ async getAccessToken() {
19
+ const encodedCredentials = btoa(`${this.clientId}:${this.clientSecret}`);
30
20
  const urlencoded = new URLSearchParams();
31
21
  urlencoded.append('grant_type', 'client_credentials');
32
22
  const response = await this.fetch(`${this.authUrl}/oauth/token`, {
@@ -46,8 +36,7 @@ class DefaultOauth2Service {
46
36
  },
47
37
  });
48
38
  }
49
- const tokenRes = (await response.json());
50
- return tokenRes;
39
+ return (await response.json());
51
40
  }
52
41
  }
53
- exports.DefaultOauth2Service = DefaultOauth2Service;
42
+ exports.DefaultAuthorizationService = DefaultAuthorizationService;
@@ -1,5 +1,5 @@
1
1
  import { Payment, PaymentDraft } from '@commercetools/platform-sdk';
2
- import { GetPayment, PaymentService, PaymentServiceOptions, UpdatePayment } from '../types/payment.type';
2
+ import { GetPayment, PaymentModificationValidation, PaymentModificationValidationResult, PaymentService, PaymentServiceOptions, UpdatePayment } from '../types/payment.type';
3
3
  /**
4
4
  * This is the default implementation of the PaymentService interface.
5
5
  */
@@ -9,6 +9,7 @@ export declare class DefaultPaymentService implements PaymentService {
9
9
  getPayment(opts: GetPayment): Promise<Payment>;
10
10
  createPayment(draft: PaymentDraft): Promise<Payment>;
11
11
  updatePayment(opts: UpdatePayment): Promise<Payment>;
12
+ validatePaymentModification(opts: PaymentModificationValidation): PaymentModificationValidationResult;
12
13
  private consolidateUpdateActions;
13
14
  private populateSetInterfaceIdAction;
14
15
  private populateChangeTransactionInteractionId;
@@ -17,4 +18,8 @@ export declare class DefaultPaymentService implements PaymentService {
17
18
  private populateSetPaymentMethod;
18
19
  private findMatchingTransactions;
19
20
  private consolidateTransactionChanges;
21
+ private validateCancelAuthorization;
22
+ private validateCapturePayment;
23
+ private validateRefundPayment;
24
+ private calculateTotalAmount;
20
25
  }
@@ -44,6 +44,18 @@ class DefaultPaymentService {
44
44
  }
45
45
  throw err;
46
46
  }
47
+ validatePaymentModification(opts) {
48
+ switch (opts.type) {
49
+ case 'cancelAuthorization':
50
+ return this.validateCancelAuthorization(opts.payment);
51
+ case 'capturePayment':
52
+ return this.validateCapturePayment(opts.payment, opts.amount);
53
+ case 'refundPayment':
54
+ return this.validateRefundPayment(opts.payment, opts.amount);
55
+ default:
56
+ throw new Error(`Invalid payment modification type: ${opts.type}`);
57
+ }
58
+ }
47
59
  consolidateUpdateActions(payment, updateInfo) {
48
60
  const actions = [];
49
61
  if (!payment.interfaceId && updateInfo.pspReference) {
@@ -125,5 +137,65 @@ class DefaultPaymentService {
125
137
  }
126
138
  return actions;
127
139
  }
140
+ validateCancelAuthorization(payment) {
141
+ const totalAuthorized = this.calculateTotalAmount(payment, 'Authorization', payment.amountPlanned.currencyCode);
142
+ if (totalAuthorized === 0) {
143
+ return { isValid: false, reason: `No authorization transaction found for resource ${payment.id}.` };
144
+ }
145
+ const totalCaptured = this.calculateTotalAmount(payment, 'Charge', payment.amountPlanned.currencyCode);
146
+ if (totalCaptured > 0) {
147
+ return { isValid: false, reason: `Resource ${payment.id} has already been charged.` };
148
+ }
149
+ return { isValid: true };
150
+ }
151
+ validateCapturePayment(payment, amount) {
152
+ if (payment.amountPlanned.currencyCode !== amount.currencyCode) {
153
+ return {
154
+ isValid: false,
155
+ reason: `Invalid currency ${amount.currencyCode} for resource ${payment.id}, expected ${payment.amountPlanned.currencyCode}`,
156
+ };
157
+ }
158
+ const totalAuthorized = this.calculateTotalAmount(payment, 'Authorization', amount.currencyCode);
159
+ if (totalAuthorized === 0) {
160
+ return { isValid: false, reason: `No authorization transaction found for resource ${payment.id}.` };
161
+ }
162
+ const totalCaptured = this.calculateTotalAmount(payment, 'Charge', amount.currencyCode);
163
+ const allowedAmount = totalAuthorized - totalCaptured;
164
+ if (amount.centAmount > allowedAmount) {
165
+ return {
166
+ isValid: false,
167
+ reason: `The amount to capture ${amount.centAmount} exceeds the allowed amount [${allowedAmount}]`,
168
+ };
169
+ }
170
+ return { isValid: true };
171
+ }
172
+ validateRefundPayment(payment, amount) {
173
+ if (payment.amountPlanned.currencyCode !== amount.currencyCode) {
174
+ return {
175
+ isValid: false,
176
+ reason: `Invalid currency ${amount.currencyCode} for resource ${payment.id}, expected ${payment.amountPlanned.currencyCode}`,
177
+ };
178
+ }
179
+ const totalCaptured = this.calculateTotalAmount(payment, 'Charge', amount.currencyCode);
180
+ if (totalCaptured === 0) {
181
+ return { isValid: false, reason: `No charge transaction found for resource ${payment.id}.` };
182
+ }
183
+ const totalRefunded = this.calculateTotalAmount(payment, 'Refund', amount.currencyCode);
184
+ const allowedAmount = totalCaptured - totalRefunded;
185
+ if (amount.centAmount > allowedAmount) {
186
+ return {
187
+ isValid: false,
188
+ reason: `The amount to refund ${amount.centAmount} exceeds the allowed amount [${allowedAmount}]`,
189
+ };
190
+ }
191
+ return { isValid: true };
192
+ }
193
+ calculateTotalAmount(payment, type, currencyCode) {
194
+ return payment.transactions
195
+ .filter((transaction) => transaction.type === type &&
196
+ transaction.state === 'Success' &&
197
+ transaction.amount.currencyCode === currencyCode)
198
+ .reduce((total, transaction) => total + transaction.amount.centAmount, 0);
199
+ }
128
200
  }
129
201
  exports.DefaultPaymentService = DefaultPaymentService;
@@ -0,0 +1,16 @@
1
+ import { AuthorizationService, CommercetoolsToken } from '../types/authorization.type';
2
+ import { Session, SessionService } from '../types/session.type';
3
+ export declare class DefaultSessionService implements SessionService {
4
+ private authorizationService;
5
+ private sessionUrl;
6
+ private projectKey;
7
+ protected token: CommercetoolsToken;
8
+ constructor(opts: {
9
+ authorizationService: AuthorizationService;
10
+ sessionUrl: string;
11
+ projectKey: string;
12
+ });
13
+ verifySession(sessionId: string): Promise<Session>;
14
+ getCartFromSession(session: Session): string;
15
+ getAllowedPaymentMethodsFromSession(session: Session): string[];
16
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultSessionService = void 0;
4
+ const errorx_1 = require("../../errorx/errorx");
5
+ class DefaultSessionService {
6
+ authorizationService;
7
+ sessionUrl;
8
+ projectKey;
9
+ token;
10
+ constructor(opts) {
11
+ this.authorizationService = opts.authorizationService;
12
+ this.sessionUrl = opts.sessionUrl;
13
+ this.projectKey = opts.projectKey;
14
+ }
15
+ async verifySession(sessionId) {
16
+ if (!this.token) {
17
+ this.token = await this.authorizationService.getAccessToken();
18
+ }
19
+ const res = await fetch(`${this.sessionUrl}/${this.projectKey}/sessions/${sessionId}`, {
20
+ method: 'GET',
21
+ headers: {
22
+ 'Content-Type': 'application/json',
23
+ Authorization: `Bearer ${this.token.access_token}`,
24
+ },
25
+ });
26
+ if (!res.ok) {
27
+ throw new errorx_1.ErrorGeneral('Could not get session');
28
+ }
29
+ const session = (await res.json());
30
+ if (session.state !== 'ACTIVE') {
31
+ throw new errorx_1.ErrorAuthErrorResponse();
32
+ }
33
+ return session;
34
+ }
35
+ getCartFromSession(session) {
36
+ if (!session.activeCart?.cartRef?.id) {
37
+ throw new errorx_1.ErrorAuthErrorResponse();
38
+ }
39
+ return session.activeCart.cartRef.id;
40
+ }
41
+ getAllowedPaymentMethodsFromSession(session) {
42
+ return session.metadata?.allowedPaymentMethods || [];
43
+ }
44
+ }
45
+ exports.DefaultSessionService = DefaultSessionService;
@@ -1,6 +1,7 @@
1
1
  import { Cart, Payment, PaymentDraft, PaymentPagedQueryResponse, PaymentUpdateAction } from '@commercetools/platform-sdk';
2
2
  import { ByProjectKeyRequestBuilder } from '@commercetools/platform-sdk/dist/declarations/src/generated/client/by-project-key-request-builder';
3
3
  export type CommercetoolsClient = ByProjectKeyRequestBuilder;
4
+ export type CommercetoolsSessionClient = ByProjectKeyRequestBuilder;
4
5
  export interface APIOpts {
5
6
  client: CommercetoolsClient;
6
7
  }
@@ -16,6 +17,16 @@ export type UpdatePayment = {
16
17
  resource: UpdateResource;
17
18
  actions: PaymentUpdateAction[];
18
19
  };
20
+ export type OauthToken = {
21
+ accessToken: string;
22
+ expiresIn: number;
23
+ tokenType: string;
24
+ scope: string;
25
+ refreshToken?: string;
26
+ };
27
+ export interface AuthorizationAPI {
28
+ getToken(): Promise<OauthToken>;
29
+ }
19
30
  export interface CartAPI {
20
31
  getCartById(id: string): Promise<Cart>;
21
32
  addPayment(opts: AddPayment): Promise<Cart>;
@@ -0,0 +1,9 @@
1
+ export type CommercetoolsToken = {
2
+ access_token: string;
3
+ token_type: string;
4
+ scope: string;
5
+ expires_in: number;
6
+ };
7
+ export interface AuthorizationService {
8
+ getAccessToken(): Promise<CommercetoolsToken>;
9
+ }
@@ -28,6 +28,15 @@ export type UpdatePayment = {
28
28
  transaction?: TransactionData;
29
29
  paymentMethod?: string;
30
30
  };
31
+ export type PaymentModificationValidation = {
32
+ payment: Payment;
33
+ amount: Money;
34
+ type: TransactionType;
35
+ };
36
+ export type PaymentModificationValidationResult = {
37
+ isValid: boolean;
38
+ reason?: string;
39
+ };
31
40
  /**
32
41
  * Payment service interface exposes methods to interact with the commercetools platform API.
33
42
  */
@@ -35,4 +44,5 @@ export interface PaymentService {
35
44
  getPayment(opts: GetPayment): Promise<Payment>;
36
45
  createPayment(draft: PaymentDraft): Promise<Payment>;
37
46
  updatePayment(opts: UpdatePayment): Promise<Payment>;
47
+ validatePaymentModification(opts: PaymentModificationValidation): PaymentModificationValidationResult;
38
48
  }
@@ -0,0 +1,28 @@
1
+ export type Session = {
2
+ id: string;
3
+ version: number;
4
+ createdAt: string;
5
+ lastModifiedAt: string;
6
+ state: 'ACTIVE' | 'EXPIRED';
7
+ activeCart?: {
8
+ cartRef: {
9
+ id: string;
10
+ };
11
+ };
12
+ customer?: {
13
+ customerRef: {
14
+ externalId?: string;
15
+ id?: string;
16
+ key?: string;
17
+ };
18
+ anonymousId?: string;
19
+ };
20
+ metadata?: {
21
+ [key: string]: unknown;
22
+ };
23
+ };
24
+ export interface SessionService {
25
+ verifySession(sessionId: string): Promise<Session>;
26
+ getCartFromSession(session: Session): string;
27
+ getAllowedPaymentMethodsFromSession(session: Session): string[];
28
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -148,3 +148,6 @@ export declare class ErrorResourceNotFound extends Errorx {
148
148
  export declare class ErrorConcurrentModification extends Errorx {
149
149
  constructor(resourceId: string, expectedVersion: number, currentVersion: number, additionalOpts?: ErrorxAdditionalOpts);
150
150
  }
151
+ export declare class ErrorMissingAuthenticationInfo extends Errorx {
152
+ constructor(additionalOpts?: ErrorxAdditionalOpts);
153
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ErrorConcurrentModification = exports.ErrorResourceNotFound = exports.ErrorSyntaxError = exports.ErrorRequiredField = exports.ErrorReferencedResourceNotFound = exports.ErrorReferenceExists = exports.ErrorObjectNotFound = exports.ErrorMoneyOverflow = exports.ErrorInternalConstraintViolated = exports.ErrorInvalidField = exports.ErrorInvalidOperation = exports.ErrorInvalidJsonInput = exports.ErrorMissingProjectKey = exports.ErrorGeneral = exports.ErrorAuthErrorResponse = exports.MultiErrorx = exports.Errorx = void 0;
3
+ exports.ErrorMissingAuthenticationInfo = exports.ErrorConcurrentModification = exports.ErrorResourceNotFound = exports.ErrorSyntaxError = exports.ErrorRequiredField = exports.ErrorReferencedResourceNotFound = exports.ErrorReferenceExists = exports.ErrorObjectNotFound = exports.ErrorMoneyOverflow = exports.ErrorInternalConstraintViolated = exports.ErrorInvalidField = exports.ErrorInvalidOperation = exports.ErrorInvalidJsonInput = exports.ErrorMissingProjectKey = exports.ErrorGeneral = exports.ErrorAuthErrorResponse = exports.MultiErrorx = exports.Errorx = void 0;
4
4
  /**
5
5
  * Errorx is a custom error class that extends the native Error class.
6
6
  */
@@ -324,3 +324,14 @@ class ErrorConcurrentModification extends Errorx {
324
324
  }
325
325
  }
326
326
  exports.ErrorConcurrentModification = ErrorConcurrentModification;
327
+ class ErrorMissingAuthenticationInfo extends Errorx {
328
+ constructor(additionalOpts) {
329
+ super({
330
+ code: 'MissingAuthenticationInfo',
331
+ httpErrorStatus: 400,
332
+ message: 'Not able to identify the user',
333
+ ...additionalOpts,
334
+ });
335
+ }
336
+ }
337
+ exports.ErrorMissingAuthenticationInfo = ErrorMissingAuthenticationInfo;
package/dist/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- /// <reference types="node" />
2
- import { RequestContextData, RequestContextProvider } from './api';
1
+ import { RequestContextData, RequestContextProvider, SessionAuthenticationHook } from './api';
3
2
  import { DefaultCommercetoolsAPI } from './commercetools/api/root-api';
4
3
  import { DefaultCartService } from './commercetools/services/ct-cart.service';
5
4
  import { DefaultPaymentService } from './commercetools/services/ct-payment.service';
5
+ import { DefaultAuthorizationService } from './commercetools/services/ct-authorization.service';
6
6
  import { Logger } from './logger';
7
- import { DefaultOauth2Service, DefaultSessionAuthenticator } from './security';
8
7
  export * from './api';
9
8
  export * from './commercetools';
10
9
  export * from './errorx';
@@ -24,10 +23,7 @@ export declare const setupPaymentSDK: (opts: {
24
23
  ctAPI: DefaultCommercetoolsAPI;
25
24
  ctCartService: DefaultCartService;
26
25
  ctPaymentService: DefaultPaymentService;
27
- oauth2Service: DefaultOauth2Service;
28
- sessionAuthenticator: DefaultSessionAuthenticator;
26
+ ctAuthorizationService: DefaultAuthorizationService;
29
27
  contextProvider: RequestContextProvider;
30
- sessionAuthHookFn: (req: {
31
- headers: import("http").IncomingHttpHeaders;
32
- }) => Promise<void>;
28
+ sessionAuthHookFn: SessionAuthenticationHook;
33
29
  };
package/dist/index.js CHANGED
@@ -19,6 +19,8 @@ const api_1 = require("./api");
19
19
  const root_api_1 = require("./commercetools/api/root-api");
20
20
  const ct_cart_service_1 = require("./commercetools/services/ct-cart.service");
21
21
  const ct_payment_service_1 = require("./commercetools/services/ct-payment.service");
22
+ const ct_authorization_service_1 = require("./commercetools/services/ct-authorization.service");
23
+ const ct_session_service_1 = require("./commercetools/services/ct-session.service");
22
24
  const base_decorator_1 = require("./fetch/decorators/base.decorator");
23
25
  const monitoring_decorator_1 = require("./fetch/decorators/monitoring.decorator");
24
26
  const security_1 = require("./security");
@@ -32,6 +34,8 @@ const setupPaymentSDK = (opts) => {
32
34
  getContextFn: opts.getContextFn,
33
35
  updateContextFn: opts.updateContextFn,
34
36
  });
37
+ const fetcher = new monitoring_decorator_1.MonitoringFetcherDecorator(new base_decorator_1.BasicFetcher(), contextProvider);
38
+ const decoratedFetch = fetcher.run.bind(fetcher);
35
39
  const ctAPI = new root_api_1.DefaultCommercetoolsAPI({
36
40
  apiUrl: opts.apiUrl,
37
41
  authUrl: opts.authUrl,
@@ -42,31 +46,29 @@ const setupPaymentSDK = (opts) => {
42
46
  });
43
47
  const ctCartService = new ct_cart_service_1.DefaultCartService({ ctAPI });
44
48
  const ctPaymentService = new ct_payment_service_1.DefaultPaymentService({ ctAPI });
45
- const fetcher = new monitoring_decorator_1.MonitoringFetcherDecorator(new base_decorator_1.BasicFetcher(), contextProvider);
46
- const decoratedFetch = fetcher.run.bind(fetcher);
47
- const oauth2Service = new security_1.DefaultOauth2Service({
49
+ const ctAuthorizationService = new ct_authorization_service_1.DefaultAuthorizationService({
48
50
  authUrl: opts.authUrl,
49
- fetch: decoratedFetch,
50
- logger: opts.logger,
51
- });
52
- const sessionAuthenticator = new security_1.DefaultSessionAuthenticator({
53
- oauth2Service,
54
51
  clientId: opts.clientId,
55
52
  clientSecret: opts.clientSecret,
56
- projectKey: opts.projectKey,
57
- sessionUrl: opts.sessionUrl,
58
53
  fetch: decoratedFetch,
59
54
  });
60
- const sessionAuthHookFn = (0, api_1.sessionAuthHook)({
55
+ const sessionService = new ct_session_service_1.DefaultSessionService({
56
+ authorizationService: ctAuthorizationService,
57
+ sessionUrl: opts.sessionUrl,
58
+ projectKey: opts.projectKey,
59
+ });
60
+ const sessionAuthenticationManager = new security_1.SessionAuthenticationManager({
61
+ sessionService,
62
+ });
63
+ const sessionAuthHookFn = new api_1.SessionAuthenticationHook({
64
+ authenticationManager: sessionAuthenticationManager,
61
65
  contextProvider,
62
- sessionAuthenticator,
63
66
  });
64
67
  return {
65
68
  ctAPI,
66
69
  ctCartService,
67
70
  ctPaymentService,
68
- oauth2Service,
69
- sessionAuthenticator,
71
+ ctAuthorizationService,
70
72
  contextProvider,
71
73
  sessionAuthHookFn,
72
74
  };
@@ -0,0 +1,24 @@
1
+ import { Authentication, HeaderPrincipal, SessionPrincipal } from './types/authn.type';
2
+ export declare class SessionAuthentication implements Authentication<SessionPrincipal, string> {
3
+ private principal;
4
+ private authorities;
5
+ private sessionId;
6
+ private authenticated;
7
+ constructor(sessionId: string, principal: SessionPrincipal);
8
+ hasPrincipal(): boolean;
9
+ getAuthorities(): string[];
10
+ hasCredentials(): boolean;
11
+ getPrincipal(): SessionPrincipal;
12
+ getCredentials(): string;
13
+ isAuthenticated(): boolean;
14
+ }
15
+ export declare class HeaderBasedAuthentication implements Authentication<HeaderPrincipal, string> {
16
+ private authHeader;
17
+ constructor(authHeader: string);
18
+ hasPrincipal(): boolean;
19
+ getAuthorities(): string[];
20
+ hasCredentials(): boolean;
21
+ getCredentials(): string;
22
+ getPrincipal(): HeaderPrincipal;
23
+ isAuthenticated(): boolean;
24
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HeaderBasedAuthentication = exports.SessionAuthentication = void 0;
4
+ class SessionAuthentication {
5
+ principal;
6
+ authorities;
7
+ sessionId;
8
+ authenticated;
9
+ constructor(sessionId, principal) {
10
+ this.principal = principal;
11
+ this.sessionId = sessionId;
12
+ this.authenticated = true;
13
+ }
14
+ hasPrincipal() {
15
+ return this.getPrincipal() !== undefined;
16
+ }
17
+ getAuthorities() {
18
+ return this.authorities;
19
+ }
20
+ hasCredentials() {
21
+ return this.getCredentials() !== undefined;
22
+ }
23
+ getPrincipal() {
24
+ return this.principal;
25
+ }
26
+ getCredentials() {
27
+ return this.sessionId;
28
+ }
29
+ isAuthenticated() {
30
+ return this.authenticated;
31
+ }
32
+ }
33
+ exports.SessionAuthentication = SessionAuthentication;
34
+ class HeaderBasedAuthentication {
35
+ authHeader;
36
+ constructor(authHeader) {
37
+ this.authHeader = authHeader;
38
+ }
39
+ hasPrincipal() {
40
+ return this.getPrincipal() != undefined;
41
+ }
42
+ getAuthorities() {
43
+ return [];
44
+ }
45
+ hasCredentials() {
46
+ return this.getCredentials() != undefined;
47
+ }
48
+ getCredentials() {
49
+ return this.authHeader;
50
+ }
51
+ getPrincipal() {
52
+ return {
53
+ authHeader: this.authHeader,
54
+ };
55
+ }
56
+ isAuthenticated() {
57
+ return false;
58
+ }
59
+ }
60
+ exports.HeaderBasedAuthentication = HeaderBasedAuthentication;
@@ -0,0 +1,10 @@
1
+ import { AuthenticationManager } from './types/authn.type';
2
+ import { HeaderBasedAuthentication, SessionAuthentication } from './authns';
3
+ import { CommercetoolsSessionService } from '../../commercetools';
4
+ export declare class SessionAuthenticationManager implements AuthenticationManager {
5
+ private sessionService;
6
+ constructor(opts: {
7
+ sessionService: CommercetoolsSessionService;
8
+ });
9
+ authenticate(authentication: HeaderBasedAuthentication): Promise<SessionAuthentication>;
10
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionAuthenticationManager = void 0;
4
+ const authns_1 = require("./authns");
5
+ const errorx_1 = require("../../errorx");
6
+ class SessionAuthenticationManager {
7
+ sessionService;
8
+ constructor(opts) {
9
+ this.sessionService = opts.sessionService;
10
+ }
11
+ async authenticate(authentication) {
12
+ const principal = authentication.getPrincipal();
13
+ try {
14
+ const session = await this.sessionService.verifySession(principal.authHeader);
15
+ return new authns_1.SessionAuthentication(principal.authHeader, {
16
+ cartId: this.sessionService.getCartFromSession(session),
17
+ allowedPaymentMethods: this.sessionService.getAllowedPaymentMethodsFromSession(session),
18
+ });
19
+ }
20
+ catch (e) {
21
+ throw new errorx_1.ErrorAuthErrorResponse();
22
+ }
23
+ }
24
+ }
25
+ exports.SessionAuthenticationManager = SessionAuthenticationManager;
@@ -0,0 +1,18 @@
1
+ export interface AuthenticationManager {
2
+ authenticate(authentication: Authentication): Promise<Authentication> | Authentication;
3
+ }
4
+ export interface Authentication<Principal = unknown, Credentials = unknown> {
5
+ hasPrincipal(): boolean;
6
+ getAuthorities(): string[];
7
+ hasCredentials(): boolean;
8
+ getPrincipal(): Principal;
9
+ getCredentials(): Credentials;
10
+ isAuthenticated(): boolean;
11
+ }
12
+ export type SessionPrincipal = {
13
+ cartId: string;
14
+ allowedPaymentMethods: string[];
15
+ };
16
+ export type HeaderPrincipal = {
17
+ authHeader: string;
18
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,3 +1,3 @@
1
- export * from './auth/session.auth';
2
- export * from './services/oauth2.service';
3
- export * from './types/session.type';
1
+ export * from './authn/types/authn.type';
2
+ export * from './authn/authns';
3
+ export * from './authn/session-authn-manager';
@@ -14,6 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./auth/session.auth"), exports);
18
- __exportStar(require("./services/oauth2.service"), exports);
19
- __exportStar(require("./types/session.type"), exports);
17
+ __exportStar(require("./authn/types/authn.type"), exports);
18
+ __exportStar(require("./authn/authns"), exports);
19
+ __exportStar(require("./authn/session-authn-manager"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commercetools/connect-payments-sdk",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Payment SDK for commercetools payment connectors",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "license": "ISC",
17
17
  "dependencies": {
18
- "@commercetools/platform-sdk": "6.0.0",
18
+ "@commercetools/platform-sdk": "7.2.0-alpha.4",
19
19
  "@commercetools/sdk-client-v2": "2.3.0"
20
20
  }
21
21
  }
package/.husky/pre-commit DELETED
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env sh
2
- . "$(dirname -- "$0")/_/husky.sh"
3
-
4
- pnpm run format && pnpm run lint
@@ -1,20 +0,0 @@
1
- import { Fetch } from '../../fetch/types/fetch.type';
2
- import { Oauth2Service } from '../types/oauth2.type';
3
- import { IntrospectSessionParams, PaymentSessionData, SessionAuthenticator } from '../types/session.type';
4
- export declare class DefaultSessionAuthenticator implements SessionAuthenticator {
5
- private oauth2Service;
6
- private sessionUrl;
7
- private clientId;
8
- private clientSecret;
9
- private projectKey;
10
- private fetch;
11
- constructor(opts: {
12
- oauth2Service: Oauth2Service;
13
- sessionUrl: string;
14
- clientId: string;
15
- clientSecret: string;
16
- projectKey: string;
17
- fetch: Fetch;
18
- });
19
- introspectSession(opts: IntrospectSessionParams): Promise<PaymentSessionData>;
20
- }
@@ -1,54 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DefaultSessionAuthenticator = void 0;
4
- const errorx_1 = require("../../errorx/errorx");
5
- class DefaultSessionAuthenticator {
6
- oauth2Service;
7
- sessionUrl;
8
- clientId;
9
- clientSecret;
10
- projectKey;
11
- fetch;
12
- constructor(opts) {
13
- this.oauth2Service = opts.oauth2Service;
14
- this.sessionUrl = opts.sessionUrl;
15
- this.clientId = opts.clientId;
16
- this.clientSecret = opts.clientSecret;
17
- this.projectKey = opts.projectKey;
18
- this.fetch = opts.fetch;
19
- }
20
- async introspectSession(opts) {
21
- const accessToken = await this.oauth2Service.getAccessToken({
22
- clientId: this.clientId,
23
- clientSecret: this.clientSecret,
24
- });
25
- const response = await this.fetch(`${this.sessionUrl}/${this.projectKey}/sessions/${opts.sessionId}`, {
26
- method: 'GET',
27
- headers: {
28
- 'Content-Type': 'application/json',
29
- Authorization: `Bearer ${accessToken.access_token}`,
30
- },
31
- });
32
- if (!response.ok) {
33
- if (response.status === 401) {
34
- throw new errorx_1.ErrorAuthErrorResponse({
35
- privateMessage: 'Failed to get session data',
36
- privateFields: {
37
- responseStatus: response.status,
38
- responseText: await response.text(),
39
- },
40
- });
41
- }
42
- throw new errorx_1.ErrorGeneral(undefined, {
43
- privateMessage: 'Failed to get session data',
44
- privateFields: {
45
- responseStatus: response.status,
46
- responseText: await response.text(),
47
- },
48
- });
49
- }
50
- const sessionRes = (await response.json());
51
- return sessionRes.metadata;
52
- }
53
- }
54
- exports.DefaultSessionAuthenticator = DefaultSessionAuthenticator;
@@ -1,16 +0,0 @@
1
- import { Fetch } from '../../fetch/types/fetch.type';
2
- import { Logger } from '../../logger';
3
- import { GetAccessTokenParams, Oauth2Service, TokenResponse } from '../types/oauth2.type';
4
- export declare class DefaultOauth2Service implements Oauth2Service {
5
- private authUrl;
6
- private fetch;
7
- private logger?;
8
- constructor(opts: {
9
- authUrl: string;
10
- fetch: Fetch;
11
- logger?: Logger;
12
- });
13
- private oauth2tokenCache;
14
- private oauth2tokenKey;
15
- getAccessToken(opts: GetAccessTokenParams): Promise<TokenResponse>;
16
- }
@@ -1,13 +0,0 @@
1
- export type TokenResponse = {
2
- access_token: string;
3
- token_type: string;
4
- scope: string;
5
- expires_in: number;
6
- };
7
- export interface Oauth2Service {
8
- getAccessToken(opts: GetAccessTokenParams): Promise<TokenResponse>;
9
- }
10
- export type GetAccessTokenParams = {
11
- clientId: string;
12
- clientSecret: string;
13
- };
@@ -1,10 +0,0 @@
1
- export type PaymentSessionData = {
2
- cartId: string;
3
- allowedPaymentMethods?: string[];
4
- };
5
- export interface SessionAuthenticator {
6
- introspectSession(opts: IntrospectSessionParams): Promise<PaymentSessionData>;
7
- }
8
- export type IntrospectSessionParams = {
9
- sessionId: string;
10
- };