@scayle/storefront-core 7.33.0 → 7.34.1
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/CHANGELOG.md +12 -0
- package/dist/api/oauth.cjs +16 -0
- package/dist/api/oauth.d.ts +7 -3
- package/dist/api/oauth.mjs +16 -0
- package/dist/rpc/methods/index.cjs +11 -0
- package/dist/rpc/methods/index.d.ts +1 -0
- package/dist/rpc/methods/index.mjs +1 -0
- package/dist/rpc/methods/oauth/idp.cjs +55 -0
- package/dist/rpc/methods/oauth/idp.d.ts +7 -0
- package/dist/rpc/methods/oauth/idp.mjs +48 -0
- package/dist/rpc/methods/session.cjs +2 -2
- package/dist/rpc/methods/session.mjs +2 -2
- package/dist/rpc/methods/variants.d.ts +2 -1
- package/dist/types/api/auth.d.ts +12 -0
- package/dist/types/api/context.d.ts +2 -1
- package/package.json +12 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @scayle/storefront-core
|
|
2
2
|
|
|
3
|
+
## 7.34.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fix type errors
|
|
8
|
+
|
|
9
|
+
## 7.34.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- We added support for Identity Provider (IDP), enhancing our authentication and identity management capabilities.
|
|
14
|
+
|
|
3
15
|
## 7.33.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/api/oauth.cjs
CHANGED
|
@@ -40,6 +40,7 @@ class OAuthClient {
|
|
|
40
40
|
headers;
|
|
41
41
|
baseURL;
|
|
42
42
|
logger;
|
|
43
|
+
clientId;
|
|
43
44
|
constructor(options, logger) {
|
|
44
45
|
const {
|
|
45
46
|
clientId,
|
|
@@ -49,6 +50,7 @@ class OAuthClient {
|
|
|
49
50
|
if (!clientId || !clientSecret) {
|
|
50
51
|
throw new MissingCredentialsError();
|
|
51
52
|
}
|
|
53
|
+
this.clientId = clientId;
|
|
52
54
|
this.baseURL = `${apiHost}/v1`;
|
|
53
55
|
this.logger = logger ? logger.space("auth-client") : void 0;
|
|
54
56
|
const basicAuthHash = (0, _hash.encodeBase64)(`${clientId}:${clientSecret}`);
|
|
@@ -174,5 +176,19 @@ class OAuthClient {
|
|
|
174
176
|
}
|
|
175
177
|
}).then(emptyOAuthResponseHandler);
|
|
176
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Revoke all tokens for a user
|
|
181
|
+
* @param userId
|
|
182
|
+
*/
|
|
183
|
+
async generateToken(code) {
|
|
184
|
+
return await fetch(`${this.baseURL}/oauth/token`, {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: this.headers,
|
|
187
|
+
body: JSON.stringify({
|
|
188
|
+
grant_type: "authorization_code",
|
|
189
|
+
code
|
|
190
|
+
})
|
|
191
|
+
}).then(oauthResponseHandler);
|
|
192
|
+
}
|
|
177
193
|
}
|
|
178
194
|
exports.OAuthClient = OAuthClient;
|
package/dist/api/oauth.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare class OAuthClient {
|
|
|
15
15
|
headers: HeadersInit;
|
|
16
16
|
baseURL: string;
|
|
17
17
|
logger?: Log;
|
|
18
|
+
clientId?: string;
|
|
18
19
|
constructor(options: OAuthOptions, logger?: Log);
|
|
19
20
|
/**
|
|
20
21
|
* Register a user and retrieve a token set
|
|
@@ -25,9 +26,7 @@ export declare class OAuthClient {
|
|
|
25
26
|
* Execute a user login on the OAuth API and receive a token set
|
|
26
27
|
* @param payload
|
|
27
28
|
*/
|
|
28
|
-
login(payload: LoginRequest
|
|
29
|
-
shopId: string;
|
|
30
|
-
}): Promise<Oauth>;
|
|
29
|
+
login(payload: LoginRequest): Promise<Oauth>;
|
|
31
30
|
/**
|
|
32
31
|
* Execute a guest user login on the OAuth API and receive a token set
|
|
33
32
|
* @param payload
|
|
@@ -64,4 +63,9 @@ export declare class OAuthClient {
|
|
|
64
63
|
* @param accessToken
|
|
65
64
|
*/
|
|
66
65
|
revokeToken(accessToken: string): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Revoke all tokens for a user
|
|
68
|
+
* @param userId
|
|
69
|
+
*/
|
|
70
|
+
generateToken(code: string): Promise<Oauth>;
|
|
67
71
|
}
|
package/dist/api/oauth.mjs
CHANGED
|
@@ -29,11 +29,13 @@ export class OAuthClient {
|
|
|
29
29
|
headers;
|
|
30
30
|
baseURL;
|
|
31
31
|
logger;
|
|
32
|
+
clientId;
|
|
32
33
|
constructor(options, logger) {
|
|
33
34
|
const { clientId, clientSecret, apiHost } = options;
|
|
34
35
|
if (!clientId || !clientSecret) {
|
|
35
36
|
throw new MissingCredentialsError();
|
|
36
37
|
}
|
|
38
|
+
this.clientId = clientId;
|
|
37
39
|
this.baseURL = `${apiHost}/v1`;
|
|
38
40
|
this.logger = logger ? logger.space("auth-client") : void 0;
|
|
39
41
|
const basicAuthHash = encodeBase64(`${clientId}:${clientSecret}`);
|
|
@@ -159,4 +161,18 @@ export class OAuthClient {
|
|
|
159
161
|
}
|
|
160
162
|
}).then(emptyOAuthResponseHandler);
|
|
161
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Revoke all tokens for a user
|
|
166
|
+
* @param userId
|
|
167
|
+
*/
|
|
168
|
+
async generateToken(code) {
|
|
169
|
+
return await fetch(`${this.baseURL}/oauth/token`, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: this.headers,
|
|
172
|
+
body: JSON.stringify({
|
|
173
|
+
grant_type: "authorization_code",
|
|
174
|
+
code
|
|
175
|
+
})
|
|
176
|
+
}).then(oauthResponseHandler);
|
|
177
|
+
}
|
|
162
178
|
}
|
|
@@ -156,4 +156,15 @@ Object.keys(_promotion).forEach(function (key) {
|
|
|
156
156
|
return _promotion[key];
|
|
157
157
|
}
|
|
158
158
|
});
|
|
159
|
+
});
|
|
160
|
+
var _idp = require("./oauth/idp.cjs");
|
|
161
|
+
Object.keys(_idp).forEach(function (key) {
|
|
162
|
+
if (key === "default" || key === "__esModule") return;
|
|
163
|
+
if (key in exports && exports[key] === _idp[key]) return;
|
|
164
|
+
Object.defineProperty(exports, key, {
|
|
165
|
+
enumerable: true,
|
|
166
|
+
get: function () {
|
|
167
|
+
return _idp[key];
|
|
168
|
+
}
|
|
169
|
+
});
|
|
159
170
|
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.handleIDPLoginCallback = exports.getExternalIdpRedirect = void 0;
|
|
7
|
+
var _jose = require("jose");
|
|
8
|
+
var _oauth = require("../../../api/oauth.cjs");
|
|
9
|
+
const getExternalIdpRedirect = exports.getExternalIdpRedirect = async function getExternalIdpRedirect2(context) {
|
|
10
|
+
const shopId = context.shopId.toString();
|
|
11
|
+
const OAuthClient = (0, _oauth.getOAuthClient)(context);
|
|
12
|
+
const checkoutSecret = context.checkout.secret;
|
|
13
|
+
const isIDPEnabled = context.idp.enabled;
|
|
14
|
+
const IDPKeys = context.idp.idpKeys;
|
|
15
|
+
const IDPRedirectURL = context.idp.idpRedirectURL;
|
|
16
|
+
if (!isIDPEnabled) {
|
|
17
|
+
throw new Error("IDP disabled");
|
|
18
|
+
}
|
|
19
|
+
if (!IDPKeys.length) {
|
|
20
|
+
throw new Error("No IDP keys configured");
|
|
21
|
+
}
|
|
22
|
+
if (!IDPRedirectURL) {
|
|
23
|
+
throw new Error("No redirect URL");
|
|
24
|
+
}
|
|
25
|
+
if (!shopId) {
|
|
26
|
+
throw new Error("Missing shopId");
|
|
27
|
+
}
|
|
28
|
+
const secret = new TextEncoder().encode(checkoutSecret);
|
|
29
|
+
const results = await Promise.all(IDPKeys.map(async idpKey => {
|
|
30
|
+
const jwtPayload = await new _jose.SignJWT({
|
|
31
|
+
idpKey,
|
|
32
|
+
callbackUrl: IDPRedirectURL,
|
|
33
|
+
clientId: OAuthClient.clientId.toString()
|
|
34
|
+
}).setProtectedHeader({
|
|
35
|
+
alg: "HS256",
|
|
36
|
+
typ: "JWT"
|
|
37
|
+
}).setIssuedAt().setExpirationTime("2h").sign(secret);
|
|
38
|
+
return [idpKey, `${OAuthClient.baseURL}/auth/external/redirect?shopId=${shopId}&jwt=${jwtPayload}`];
|
|
39
|
+
}));
|
|
40
|
+
return Object.fromEntries(results);
|
|
41
|
+
};
|
|
42
|
+
const handleIDPLoginCallback = exports.handleIDPLoginCallback = async function handleIDPLoginCallback2(code, context) {
|
|
43
|
+
const OAuthClient = (0, _oauth.getOAuthClient)(context);
|
|
44
|
+
const {
|
|
45
|
+
access_token: accessToken,
|
|
46
|
+
refresh_token: refreshToken
|
|
47
|
+
} = await OAuthClient.generateToken(code);
|
|
48
|
+
context.updateTokens({
|
|
49
|
+
accessToken,
|
|
50
|
+
refreshToken
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
message: "success"
|
|
54
|
+
};
|
|
55
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RpcContext } from '../../../types';
|
|
2
|
+
export declare const getExternalIdpRedirect: (context: RpcContext) => Promise<{
|
|
3
|
+
[k: string]: string;
|
|
4
|
+
}>;
|
|
5
|
+
export declare const handleIDPLoginCallback: (code: string, context: RpcContext) => Promise<{
|
|
6
|
+
message: string;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SignJWT } from "jose";
|
|
2
|
+
import { getOAuthClient } from "../../../api/oauth.mjs";
|
|
3
|
+
export const getExternalIdpRedirect = async function getExternalIdpRedirect2(context) {
|
|
4
|
+
const shopId = context.shopId.toString();
|
|
5
|
+
const OAuthClient = getOAuthClient(context);
|
|
6
|
+
const checkoutSecret = context.checkout.secret;
|
|
7
|
+
const isIDPEnabled = context.idp.enabled;
|
|
8
|
+
const IDPKeys = context.idp.idpKeys;
|
|
9
|
+
const IDPRedirectURL = context.idp.idpRedirectURL;
|
|
10
|
+
if (!isIDPEnabled) {
|
|
11
|
+
throw new Error("IDP disabled");
|
|
12
|
+
}
|
|
13
|
+
if (!IDPKeys.length) {
|
|
14
|
+
throw new Error("No IDP keys configured");
|
|
15
|
+
}
|
|
16
|
+
if (!IDPRedirectURL) {
|
|
17
|
+
throw new Error("No redirect URL");
|
|
18
|
+
}
|
|
19
|
+
if (!shopId) {
|
|
20
|
+
throw new Error("Missing shopId");
|
|
21
|
+
}
|
|
22
|
+
const secret = new TextEncoder().encode(checkoutSecret);
|
|
23
|
+
const results = await Promise.all(
|
|
24
|
+
IDPKeys.map(async (idpKey) => {
|
|
25
|
+
const jwtPayload = await new SignJWT({
|
|
26
|
+
idpKey,
|
|
27
|
+
callbackUrl: IDPRedirectURL,
|
|
28
|
+
clientId: OAuthClient.clientId.toString()
|
|
29
|
+
}).setProtectedHeader({ alg: "HS256", typ: "JWT" }).setIssuedAt().setExpirationTime("2h").sign(secret);
|
|
30
|
+
return [
|
|
31
|
+
idpKey,
|
|
32
|
+
`${OAuthClient.baseURL}/auth/external/redirect?shopId=${shopId}&jwt=${jwtPayload}`
|
|
33
|
+
];
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
return Object.fromEntries(results);
|
|
37
|
+
};
|
|
38
|
+
export const handleIDPLoginCallback = async function handleIDPLoginCallback2(code, context) {
|
|
39
|
+
const OAuthClient = getOAuthClient(context);
|
|
40
|
+
const { access_token: accessToken, refresh_token: refreshToken } = await OAuthClient.generateToken(code);
|
|
41
|
+
context.updateTokens({
|
|
42
|
+
accessToken,
|
|
43
|
+
refreshToken
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
message: "success"
|
|
47
|
+
};
|
|
48
|
+
};
|
|
@@ -76,7 +76,7 @@ const oauthGuestLogin = async (guest, context) => {
|
|
|
76
76
|
const shopId = context.shopId;
|
|
77
77
|
const client = (0, _oauth.getOAuthClient)(context);
|
|
78
78
|
try {
|
|
79
|
-
const tokens = client.guestLogin({
|
|
79
|
+
const tokens = await client.guestLogin({
|
|
80
80
|
...guest,
|
|
81
81
|
shop_id: shopId
|
|
82
82
|
});
|
|
@@ -105,7 +105,7 @@ const refreshAccessToken = async context => {
|
|
|
105
105
|
refreshToken: tokens?.refresh_token
|
|
106
106
|
});
|
|
107
107
|
return {
|
|
108
|
-
success: !!
|
|
108
|
+
success: !!tokens
|
|
109
109
|
};
|
|
110
110
|
} catch (error) {
|
|
111
111
|
const err = convertErrorForRpcCall(error, [_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusCode.UNAUTHORIZED]);
|
|
@@ -83,7 +83,7 @@ export const oauthGuestLogin = async (guest, context) => {
|
|
|
83
83
|
const shopId = context.shopId;
|
|
84
84
|
const client = getOAuthClient(context);
|
|
85
85
|
try {
|
|
86
|
-
const tokens = client.guestLogin({
|
|
86
|
+
const tokens = await client.guestLogin({
|
|
87
87
|
...guest,
|
|
88
88
|
shop_id: shopId
|
|
89
89
|
});
|
|
@@ -114,7 +114,7 @@ export const refreshAccessToken = async (context) => {
|
|
|
114
114
|
accessToken: tokens?.access_token,
|
|
115
115
|
refreshToken: tokens?.refresh_token
|
|
116
116
|
});
|
|
117
|
-
return { success: !!
|
|
117
|
+
return { success: !!tokens };
|
|
118
118
|
} catch (error) {
|
|
119
119
|
const err = convertErrorForRpcCall(error, [
|
|
120
120
|
HttpStatusCode.BAD_REQUEST,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { ProductWith
|
|
1
|
+
import type { ProductWith } from '@aboutyou/backbone';
|
|
2
|
+
import type { VariantDetail } from '@aboutyou/backbone/endpoints/variants/variantsByIds';
|
|
2
3
|
import { RpcHandler } from '../../types';
|
|
3
4
|
export interface FetchVariantsParams {
|
|
4
5
|
ids: number[];
|
package/dist/types/api/auth.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ export interface GuestRequest {
|
|
|
49
49
|
}
|
|
50
50
|
export interface SendResetPasswordEmailRequest {
|
|
51
51
|
email: string;
|
|
52
|
+
reset_url: string;
|
|
53
|
+
shop_id: number;
|
|
52
54
|
}
|
|
53
55
|
export interface UpdatePasswordByHashRequest {
|
|
54
56
|
password: string;
|
|
@@ -69,4 +71,14 @@ export interface Oauth {
|
|
|
69
71
|
access_token: string;
|
|
70
72
|
refresh_token: string;
|
|
71
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* This adds `string` so it's flexible enough to support
|
|
76
|
+
* multiple IDPs since we don't know what they are.
|
|
77
|
+
* More narrow type can be defined in the nuxt modules.
|
|
78
|
+
*/
|
|
79
|
+
export interface IDPConfig {
|
|
80
|
+
enabled: boolean;
|
|
81
|
+
idpKeys: string[];
|
|
82
|
+
idpRedirectURL: string;
|
|
83
|
+
}
|
|
72
84
|
export {};
|
|
@@ -3,7 +3,7 @@ import { Log } from '../../utils';
|
|
|
3
3
|
import { CachedType } from '../../cache/cached';
|
|
4
4
|
import { ShopUser } from '../user';
|
|
5
5
|
import type { WishlistWithOptions, BasketWithOptions, ProductWith, VariantWith, SearchWith, CategoryWith, ProductCategoryWith } from '../';
|
|
6
|
-
import { OAuthTokens } from './auth';
|
|
6
|
+
import { OAuthTokens, IDPConfig } from './auth';
|
|
7
7
|
export type WithParams = Partial<{
|
|
8
8
|
basket: BasketWithOptions;
|
|
9
9
|
wishlist: WishlistWithOptions;
|
|
@@ -62,4 +62,5 @@ export interface RpcContext {
|
|
|
62
62
|
clientSecret: string;
|
|
63
63
|
};
|
|
64
64
|
runtimeConfiguration: RuntimeConfiguration;
|
|
65
|
+
idp?: IDPConfig;
|
|
65
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scayle/storefront-core",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.34.1",
|
|
4
4
|
"description": "Collection of essential utilities to work with the Storefront API",
|
|
5
5
|
"author": "SCAYLE Commerce Engine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"dist",
|
|
41
41
|
"utils"
|
|
42
42
|
],
|
|
43
|
+
"type": "module",
|
|
43
44
|
"engines": {
|
|
44
45
|
"node": ">= 18.15.0"
|
|
45
46
|
},
|
|
@@ -47,13 +48,15 @@
|
|
|
47
48
|
"clean": "rimraf ./dist",
|
|
48
49
|
"build:legacy": "tsc -p tsconfig.legacy.json",
|
|
49
50
|
"build": "unbuild",
|
|
51
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
50
52
|
"format": "prettier --ignore-unknown --log-level warn --check .",
|
|
51
53
|
"format:fix": "prettier --ignore-unknown --log-level warn --write .",
|
|
52
54
|
"lint": "eslint . --format gitlab",
|
|
53
55
|
"lint:fix": "eslint . --fix",
|
|
54
56
|
"package:lint": "publint",
|
|
55
|
-
"test": "
|
|
56
|
-
"test
|
|
57
|
+
"test:watch": "vitest --passWithNoTests",
|
|
58
|
+
"test": "vitest --run --passWithNoTests",
|
|
59
|
+
"test:ci": "vitest --run --passWithNoTests --coverage --reporter=default --reporter=junit"
|
|
57
60
|
},
|
|
58
61
|
"dependencies": {
|
|
59
62
|
"@aboutyou/backbone": "16.0.1",
|
|
@@ -69,21 +72,19 @@
|
|
|
69
72
|
"@scayle/eslint-config-storefront": "3.2.6",
|
|
70
73
|
"@scayle/prettier-config-storefront": "2.0.2",
|
|
71
74
|
"@types/crypto-js": "4.2.1",
|
|
72
|
-
"@types/
|
|
73
|
-
"@types/node": "20.10.5",
|
|
75
|
+
"@types/node": "20.10.6",
|
|
74
76
|
"@types/webpack-env": "1.18.4",
|
|
75
|
-
"
|
|
77
|
+
"@vitest/coverage-v8": "1.1.1",
|
|
76
78
|
"eslint": "8.56.0",
|
|
77
79
|
"eslint-formatter-gitlab": "5.1.0",
|
|
78
|
-
"jest": "29.7.0",
|
|
79
|
-
"jest-junit": "16.0.0",
|
|
80
80
|
"prettier": "3.0.0",
|
|
81
|
-
"publint": "0.2.
|
|
81
|
+
"publint": "0.2.7",
|
|
82
82
|
"rimraf": "5.0.5",
|
|
83
|
-
"ts-jest": "29.1.1",
|
|
84
83
|
"ts-node": "10.9.2",
|
|
85
84
|
"typescript": "5.3.3",
|
|
86
|
-
"
|
|
85
|
+
"unbuild": "2.0.0",
|
|
86
|
+
"unstorage": "1.10.1",
|
|
87
|
+
"vitest": "1.1.1"
|
|
87
88
|
},
|
|
88
89
|
"optionalDependencies": {
|
|
89
90
|
"redis": "4"
|