@sayren/storefront-sdk 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.d.mts +40 -2
- package/dist/auth/index.mjs +56 -2
- package/dist/{client-D-w3LIU2.mjs → client-Do9-LG5X.mjs} +48 -4
- package/dist/{http-CxqZxTfE.d.mts → http-CqAR0_w4.d.mts} +26 -2
- package/dist/index.d.mts +22 -5
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
- package/src/auth/index.ts +81 -0
- package/src/client.ts +16 -0
- package/src/schemas/auth.ts +34 -1
- package/src/schemas/member.ts +2 -1
- package/src/schemas/store.ts +4 -0
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
|
-
import { a as AnonymousSession, c as TokenPair,
|
|
1
|
+
import { a as AnonymousSession, c as LoginRequest, d as TokenPair, f as WithdrawRequest, l as SignupRequest, p as WithdrawalBlockedDetails, t as ApiError, u as SocialProvider } from "../http-CqAR0_w4.mjs";
|
|
2
2
|
//#region src/auth/index.d.ts
|
|
3
|
+
/** 소셜 로그인 시작 결과 — `codeVerifier`와 `state`는 돌아올 때까지 서버 세션(httpOnly 쿠키 등)에 보관한다 */
|
|
4
|
+
export interface IdpStart {
|
|
5
|
+
/** 구매자를 보낼 공급자 로그인 주소 */
|
|
6
|
+
url: string;
|
|
7
|
+
state: string;
|
|
8
|
+
codeVerifier: string;
|
|
9
|
+
}
|
|
10
|
+
export interface IdpCallbackInput {
|
|
11
|
+
/** 돌아온 주소의 `code`(성공) — 실패면 `error`가 온다 */
|
|
12
|
+
code: string | null;
|
|
13
|
+
/** 돌아온 주소의 `state` */
|
|
14
|
+
state: string | null;
|
|
15
|
+
/** `idp()`가 준 값 — 보관해 둔 것 */
|
|
16
|
+
expectedState: string;
|
|
17
|
+
codeVerifier: string;
|
|
18
|
+
/** 비회원 장바구니 토큰 — 주면 회원 장바구니로 합친다 */
|
|
19
|
+
cartToken?: string;
|
|
20
|
+
}
|
|
21
|
+
/** 소셜 로그인 실패 — 구매자가 취소했거나(`access_denied`) state가 맞지 않는다 */
|
|
22
|
+
export declare class IdpCallbackError extends Error {
|
|
23
|
+
readonly reason: "access_denied" | "state_mismatch" | "missing_code" | string;
|
|
24
|
+
constructor(reason: "access_denied" | "state_mismatch" | "missing_code" | string);
|
|
25
|
+
}
|
|
3
26
|
export interface StorefrontAuthOptions {
|
|
4
27
|
/** 예: https://api.sayren.app/storefront/v1 */
|
|
5
28
|
baseUrl: string;
|
|
@@ -30,6 +53,21 @@ export declare function createStorefrontAuth(options: StorefrontAuthOptions): {
|
|
|
30
53
|
session(body: LoginRequest): Promise<TokenPair>;
|
|
31
54
|
/** 회원가입 — 가입과 동시에 로그인 토큰을 받는다. `cartToken`을 주면 장바구니를 병합한다 */
|
|
32
55
|
signUp(body: SignupRequest): Promise<TokenPair>;
|
|
56
|
+
/**
|
|
57
|
+
* 소셜 로그인 시작 — PKCE(S256)와 state를 만들고 공급자 로그인 주소를 받는다. 반환한 `state`·`codeVerifier`를
|
|
58
|
+
* 서버 세션에 보관하고 구매자를 `url`로 보낸다. 로그인을 마치면 `redirectUri`로 `?code=&state=`가 붙어 돌아온다.
|
|
59
|
+
* `redirectUri`는 스토어프런트 도메인 목록 안이어야 한다
|
|
60
|
+
*/
|
|
61
|
+
idp(provider: SocialProvider, options: {
|
|
62
|
+
redirectUri: string;
|
|
63
|
+
}): Promise<IdpStart>;
|
|
64
|
+
/**
|
|
65
|
+
* 소셜 로그인 마무리 — 돌아온 `code`를 구매자 토큰으로 바꾼다. state가 다르거나 구매자가 취소했으면
|
|
66
|
+
* `IdpCallbackError`. code는 1회용이고 1분 안에 바꿔야 한다
|
|
67
|
+
*/
|
|
68
|
+
idpCallback(input: IdpCallbackInput & {
|
|
69
|
+
error?: string | null;
|
|
70
|
+
}): Promise<TokenPair>;
|
|
33
71
|
/** 토큰 갱신 — refreshToken은 한 번 쓰면 폐기되고 새 쌍이 온다(회전) */
|
|
34
72
|
refresh(refreshToken: string): Promise<TokenPair>;
|
|
35
73
|
/**
|
|
@@ -46,4 +84,4 @@ export declare function createStorefrontAuth(options: StorefrontAuthOptions): {
|
|
|
46
84
|
};
|
|
47
85
|
export type StorefrontAuth = ReturnType<typeof createStorefrontAuth>;
|
|
48
86
|
//#endregion
|
|
49
|
-
export { type AnonymousSession, ApiError, type LoginRequest, type SignupRequest, type TokenPair, type WithdrawRequest, type WithdrawalBlockedDetails };
|
|
87
|
+
export { type AnonymousSession, ApiError, type LoginRequest, type SignupRequest, type SocialProvider, type TokenPair, type WithdrawRequest, type WithdrawalBlockedDetails };
|
package/dist/auth/index.mjs
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ct as ApiError, t as createStorefrontClient } from "../client-Do9-LG5X.mjs";
|
|
2
2
|
//#region src/auth/index.ts
|
|
3
|
+
/** 소셜 로그인 실패 — 구매자가 취소했거나(`access_denied`) state가 맞지 않는다 */
|
|
4
|
+
var IdpCallbackError = class extends Error {
|
|
5
|
+
reason;
|
|
6
|
+
constructor(reason) {
|
|
7
|
+
super(`소셜 로그인을 마치지 못했어요 (${reason})`);
|
|
8
|
+
this.reason = reason;
|
|
9
|
+
this.name = "IdpCallbackError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
function base64url(bytes) {
|
|
13
|
+
let binary = "";
|
|
14
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
15
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
16
|
+
}
|
|
17
|
+
function randomToken(bytes = 32) {
|
|
18
|
+
return base64url(globalThis.crypto.getRandomValues(new Uint8Array(bytes)));
|
|
19
|
+
}
|
|
20
|
+
async function s256(verifier) {
|
|
21
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
|
|
22
|
+
return base64url(new Uint8Array(digest));
|
|
23
|
+
}
|
|
3
24
|
/**
|
|
4
25
|
* 구매자 인증 — 화면은 스토어프런트가 그리고, 계정 저장과 인증 처리는 sayren이 한다.
|
|
5
26
|
* 어느 방식으로 로그인해도 결과는 같은 구매자 토큰(`TokenPair`)이고 `/me/*`·장바구니·주문 API에 그대로 쓴다.
|
|
@@ -36,6 +57,39 @@ function createStorefrontAuth(options) {
|
|
|
36
57
|
signUp(body) {
|
|
37
58
|
return client().auth.signup(body);
|
|
38
59
|
},
|
|
60
|
+
/**
|
|
61
|
+
* 소셜 로그인 시작 — PKCE(S256)와 state를 만들고 공급자 로그인 주소를 받는다. 반환한 `state`·`codeVerifier`를
|
|
62
|
+
* 서버 세션에 보관하고 구매자를 `url`로 보낸다. 로그인을 마치면 `redirectUri`로 `?code=&state=`가 붙어 돌아온다.
|
|
63
|
+
* `redirectUri`는 스토어프런트 도메인 목록 안이어야 한다
|
|
64
|
+
*/
|
|
65
|
+
async idp(provider, options) {
|
|
66
|
+
const state = randomToken(24);
|
|
67
|
+
const codeVerifier = randomToken(32);
|
|
68
|
+
const { url } = await client().auth.idpAuthorize(provider, {
|
|
69
|
+
redirectUri: options.redirectUri,
|
|
70
|
+
codeChallenge: await s256(codeVerifier),
|
|
71
|
+
state
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
url,
|
|
75
|
+
state,
|
|
76
|
+
codeVerifier
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
/**
|
|
80
|
+
* 소셜 로그인 마무리 — 돌아온 `code`를 구매자 토큰으로 바꾼다. state가 다르거나 구매자가 취소했으면
|
|
81
|
+
* `IdpCallbackError`. code는 1회용이고 1분 안에 바꿔야 한다
|
|
82
|
+
*/
|
|
83
|
+
idpCallback(input) {
|
|
84
|
+
if (input.error) return Promise.reject(new IdpCallbackError(input.error));
|
|
85
|
+
if (!input.state || input.state !== input.expectedState) return Promise.reject(new IdpCallbackError("state_mismatch"));
|
|
86
|
+
if (!input.code) return Promise.reject(new IdpCallbackError("missing_code"));
|
|
87
|
+
return client().auth.idpToken({
|
|
88
|
+
code: input.code,
|
|
89
|
+
codeVerifier: input.codeVerifier,
|
|
90
|
+
cartToken: input.cartToken
|
|
91
|
+
});
|
|
92
|
+
},
|
|
39
93
|
/** 토큰 갱신 — refreshToken은 한 번 쓰면 폐기되고 새 쌍이 온다(회전) */
|
|
40
94
|
refresh(refreshToken) {
|
|
41
95
|
return client().auth.refresh(refreshToken);
|
|
@@ -63,4 +117,4 @@ function createStorefrontAuth(options) {
|
|
|
63
117
|
};
|
|
64
118
|
}
|
|
65
119
|
//#endregion
|
|
66
|
-
export { ApiError, createStorefrontAuth };
|
|
120
|
+
export { ApiError, IdpCallbackError, createStorefrontAuth };
|
|
@@ -155,7 +155,12 @@ const anonymousSessionSchema = z.object({
|
|
|
155
155
|
* 진행 중인 주문(결제 대기~배송 중)·클레임·결제가 있으면 409 `WITHDRAWAL_BLOCKED`, 비밀번호가 틀리면
|
|
156
156
|
* 403 `PASSWORD_MISMATCH`다(로그인과 같은 5회 잠금).
|
|
157
157
|
*/
|
|
158
|
-
const withdrawRequestSchema = z.object({
|
|
158
|
+
const withdrawRequestSchema = z.object({
|
|
159
|
+
/**
|
|
160
|
+
* 비밀번호로 가입한 구매자는 필수. 소셜 로그인으로만 가입한 구매자는 비밀번호가 없어 생략하고, 대신 최근 10분 안에
|
|
161
|
+
* 로그인한 액세스 토큰이어야 한다(아니면 403 `REAUTH_REQUIRED` — 다시 로그인한 뒤 호출)
|
|
162
|
+
*/
|
|
163
|
+
password: z.string().min(1).optional() });
|
|
159
164
|
/** `WITHDRAWAL_BLOCKED`의 `error.details` — 탈퇴를 막는 항목 수 */
|
|
160
165
|
const withdrawalBlockedDetailsSchema = z.object({
|
|
161
166
|
/** 결제 대기~배송 중인 주문 상품 */
|
|
@@ -165,6 +170,31 @@ const withdrawalBlockedDetailsSchema = z.object({
|
|
|
165
170
|
/** 승인 중이거나 결제창이 열려 있을 수 있는 결제(결제창은 만료되면 풀린다) */
|
|
166
171
|
pendingPayments: z.number().int()
|
|
167
172
|
});
|
|
173
|
+
/** 소셜 로그인 공급자 */
|
|
174
|
+
const socialProviderSchema = z.enum([
|
|
175
|
+
"kakao",
|
|
176
|
+
"naver",
|
|
177
|
+
"google"
|
|
178
|
+
]);
|
|
179
|
+
/** `POST /auth/idp/{provider}/authorize` 본문 — PKCE(S256)와 state는 SDK가 만든다 */
|
|
180
|
+
const idpAuthorizeRequestSchema = z.object({
|
|
181
|
+
/** 로그인을 마치고 돌아올 스토어프런트 주소. 스토어프런트 도메인 목록 안이어야 한다 */
|
|
182
|
+
redirectUri: z.url(),
|
|
183
|
+
/** base64url(SHA-256(codeVerifier)) */
|
|
184
|
+
codeChallenge: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
185
|
+
/** 스토어프런트가 만든 값 — 돌아올 때 그대로 붙어 온다. 세션에 저장해 두고 비교한다 */
|
|
186
|
+
state: z.string().min(16).max(128)
|
|
187
|
+
});
|
|
188
|
+
const idpAuthorizeResponseSchema = z.object({
|
|
189
|
+
/** 구매자를 보낼 공급자 로그인 주소 */
|
|
190
|
+
url: z.url() });
|
|
191
|
+
/** `POST /auth/idp/token` 본문 — 돌아온 1회용 code를 구매자 토큰으로 바꾼다 */
|
|
192
|
+
const idpTokenRequestSchema = z.object({
|
|
193
|
+
code: z.string().min(1),
|
|
194
|
+
codeVerifier: z.string().regex(/^[A-Za-z0-9._~-]{43,128}$/),
|
|
195
|
+
/** 비회원 장바구니 토큰 — 주면 회원 장바구니로 합친다 */
|
|
196
|
+
cartToken: z.string().optional()
|
|
197
|
+
});
|
|
168
198
|
//#endregion
|
|
169
199
|
//#region src/schemas/cart.ts
|
|
170
200
|
const cartItemSchema = z.object({
|
|
@@ -512,7 +542,8 @@ const createInquiryRequestSchema = z.object({
|
|
|
512
542
|
//#region src/schemas/member.ts
|
|
513
543
|
const memberSchema = z.object({
|
|
514
544
|
memberId: z.string(),
|
|
515
|
-
|
|
545
|
+
/** 소셜 가입에서 공급자가 이메일을 주지 않았으면 null */
|
|
546
|
+
email: z.email().nullable(),
|
|
516
547
|
name: z.string(),
|
|
517
548
|
phone: z.string().nullable(),
|
|
518
549
|
point: z.number().int(),
|
|
@@ -656,7 +687,16 @@ const storefrontStoreSchema = z.object({
|
|
|
656
687
|
* 비회원 주문 허용. false면 로그인하지 않은 구매자의 주문서 생성·결제 요청이 403 `GUEST_CHECKOUT_DISABLED`다.
|
|
657
688
|
* 장바구니 담기는 된다 — 주문 버튼을 로그인 안내로 바꾸면 된다
|
|
658
689
|
*/
|
|
659
|
-
guestCheckout: z.boolean()
|
|
690
|
+
guestCheckout: z.boolean(),
|
|
691
|
+
/**
|
|
692
|
+
* 켜진 로그인 방법 — 화면에 이 순서대로 버튼을 그리면 된다. `password`가 없으면 이메일·비밀번호 가입·로그인이 꺼져 있다
|
|
693
|
+
*/
|
|
694
|
+
loginMethods: z.array(z.enum([
|
|
695
|
+
"password",
|
|
696
|
+
"kakao",
|
|
697
|
+
"naver",
|
|
698
|
+
"google"
|
|
699
|
+
]))
|
|
660
700
|
});
|
|
661
701
|
//#endregion
|
|
662
702
|
//#region src/client.ts
|
|
@@ -712,6 +752,10 @@ function createStorefrontClient(options) {
|
|
|
712
752
|
login: (body) => http.request("POST", "/auth/login", tokenPairSchema, { body: loginRequestSchema.parse(body) }),
|
|
713
753
|
refresh: (refreshToken) => http.request("POST", "/auth/token/refresh", tokenPairSchema, { body: { refreshToken } }),
|
|
714
754
|
logout: () => http.requestVoid("POST", "/auth/logout"),
|
|
755
|
+
/** 소셜 로그인 시작 — 공급자 로그인 주소를 받는다. PKCE·state는 `@sayren/storefront-sdk/auth`의 `idp()`가 만든다 */
|
|
756
|
+
idpAuthorize: (provider, body) => http.request("POST", path`/auth/idp/${provider}/authorize`, idpAuthorizeResponseSchema, { body: idpAuthorizeRequestSchema.parse(body) }),
|
|
757
|
+
/** 소셜 로그인 마무리 — 돌아온 1회용 code + codeVerifier를 구매자 토큰으로 바꾼다 */
|
|
758
|
+
idpToken: (body) => http.request("POST", "/auth/idp/token", tokenPairSchema, { body: idpTokenRequestSchema.parse(body) }),
|
|
715
759
|
/** 구매자 탈퇴 — 로그인 상태(accessToken)에서 비밀번호로 본인 확인. 성공하면 토큰은 모두 무효 */
|
|
716
760
|
withdraw: (body) => http.requestVoid("POST", "/auth/withdraw", { body: withdrawRequestSchema.parse(body) })
|
|
717
761
|
},
|
|
@@ -810,4 +854,4 @@ getStatus: (paymentId) => http.request("GET", path`/payments/${paymentId}`, paym
|
|
|
810
854
|
};
|
|
811
855
|
}
|
|
812
856
|
//#endregion
|
|
813
|
-
export {
|
|
857
|
+
export { idpAuthorizeRequestSchema as $, pageSchema as A, pgParamsSchema as B, claimTypeSchema as C, claimStatusSchema as D, myClaimSchema as E, createCheckoutRequestSchema as F, productDetailSchema as G, shippingAddressInputSchema as H, guestInfoSchema as I, addCartItemRequestSchema as J, productFacetsSchema as K, paymentMethodSchema as L, checkoutSessionSchema as M, confirmPaymentRequestSchema as N, orderItemStatusSchema as O, confirmPaymentResultSchema as P, anonymousSessionSchema as Q, paymentParamsSchema as R, claimReasonSchema as S, createClaimResultSchema as T, categoryNodeSchema as U, requestPaymentRequestSchema as V, productCardSchema as W, cartSchema as X, cartItemSchema as Y, updateCartItemRequestSchema as Z, createInquiryRequestSchema as _, publicReviewSchema as a, tokenPairSchema as at, customerInquiryCategorySchema as b, writableReviewSchema as c, ApiError as ct, myOrderSchema as d, unwrapData as dt, idpAuthorizeResponseSchema as et, orderShippingAddressSchema as f, updateProfileRequestSchema as g, memberSchema as h, createReviewResultSchema as i, socialProviderSchema as it, PAYMENT_STATUS_VALUES as j, pageInfoSchema as k, deliveryTrackingSchema as l, buildQuery as lt, memberAddressSchema as m, storefrontStoreSchema as n, loginRequestSchema as nt, reviewSummarySchema as o, withdrawRequestSchema as ot, memberAddressRequestSchema as p, productSortSchema as q, createReviewRequestSchema as r, signupRequestSchema as rt, updateReviewRequestSchema as s, withdrawalBlockedDetailsSchema as st, createStorefrontClient as t, idpTokenRequestSchema as tt, myOrderItemSchema as u, toApiError as ut, publicInquirySchema as v, createClaimRequestSchema as w, customerInquirySchema as x, createCustomerInquiryRequestSchema as y, paymentStatusSchema as z };
|
|
@@ -41,7 +41,7 @@ type AnonymousSession = z.infer<typeof anonymousSessionSchema>;
|
|
|
41
41
|
* 403 `PASSWORD_MISMATCH`다(로그인과 같은 5회 잠금).
|
|
42
42
|
*/
|
|
43
43
|
declare const withdrawRequestSchema: z.ZodObject<{
|
|
44
|
-
password: z.ZodString
|
|
44
|
+
password: z.ZodOptional<z.ZodString>;
|
|
45
45
|
}, z.core.$strip>;
|
|
46
46
|
type WithdrawRequest = z.infer<typeof withdrawRequestSchema>;
|
|
47
47
|
/** `WITHDRAWAL_BLOCKED`의 `error.details` — 탈퇴를 막는 항목 수 */
|
|
@@ -51,6 +51,30 @@ declare const withdrawalBlockedDetailsSchema: z.ZodObject<{
|
|
|
51
51
|
pendingPayments: z.ZodNumber;
|
|
52
52
|
}, z.core.$strip>;
|
|
53
53
|
type WithdrawalBlockedDetails = z.infer<typeof withdrawalBlockedDetailsSchema>;
|
|
54
|
+
/** 소셜 로그인 공급자 */
|
|
55
|
+
declare const socialProviderSchema: z.ZodEnum<{
|
|
56
|
+
kakao: "kakao";
|
|
57
|
+
naver: "naver";
|
|
58
|
+
google: "google";
|
|
59
|
+
}>;
|
|
60
|
+
type SocialProvider = z.infer<typeof socialProviderSchema>;
|
|
61
|
+
/** `POST /auth/idp/{provider}/authorize` 본문 — PKCE(S256)와 state는 SDK가 만든다 */
|
|
62
|
+
declare const idpAuthorizeRequestSchema: z.ZodObject<{
|
|
63
|
+
redirectUri: z.ZodURL;
|
|
64
|
+
codeChallenge: z.ZodString;
|
|
65
|
+
state: z.ZodString;
|
|
66
|
+
}, z.core.$strip>;
|
|
67
|
+
type IdpAuthorizeRequest = z.infer<typeof idpAuthorizeRequestSchema>;
|
|
68
|
+
declare const idpAuthorizeResponseSchema: z.ZodObject<{
|
|
69
|
+
url: z.ZodURL;
|
|
70
|
+
}, z.core.$strip>;
|
|
71
|
+
/** `POST /auth/idp/token` 본문 — 돌아온 1회용 code를 구매자 토큰으로 바꾼다 */
|
|
72
|
+
declare const idpTokenRequestSchema: z.ZodObject<{
|
|
73
|
+
code: z.ZodString;
|
|
74
|
+
codeVerifier: z.ZodString;
|
|
75
|
+
cartToken: z.ZodOptional<z.ZodString>;
|
|
76
|
+
}, z.core.$strip>;
|
|
77
|
+
type IdpTokenRequest = z.infer<typeof idpTokenRequestSchema>;
|
|
54
78
|
//#endregion
|
|
55
79
|
//#region src/http.d.ts
|
|
56
80
|
declare class ApiError extends Error {
|
|
@@ -68,4 +92,4 @@ type TokenSource = string | null | undefined | (() => string | null | undefined)
|
|
|
68
92
|
type Query = Record<string, string | number | boolean | undefined>;
|
|
69
93
|
declare function buildQuery(query?: Query): string;
|
|
70
94
|
//#endregion
|
|
71
|
-
export { AnonymousSession as a,
|
|
95
|
+
export { withdrawalBlockedDetailsSchema as C, withdrawRequestSchema as S, idpTokenRequestSchema as _, AnonymousSession as a, socialProviderSchema as b, LoginRequest as c, TokenPair as d, WithdrawRequest as f, idpAuthorizeResponseSchema as g, idpAuthorizeRequestSchema as h, buildQuery as i, SignupRequest as l, anonymousSessionSchema as m, Query as n, IdpAuthorizeRequest as o, WithdrawalBlockedDetails as p, TokenSource as r, IdpTokenRequest as s, ApiError as t, SocialProvider as u, loginRequestSchema as v, tokenPairSchema as x, signupRequestSchema as y };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as AnonymousSession, c as
|
|
1
|
+
import { C as withdrawalBlockedDetailsSchema, S as withdrawRequestSchema, _ as idpTokenRequestSchema, a as AnonymousSession, b as socialProviderSchema, c as LoginRequest, d as TokenPair, f as WithdrawRequest, g as idpAuthorizeResponseSchema, h as idpAuthorizeRequestSchema, i as buildQuery, l as SignupRequest, m as anonymousSessionSchema, n as Query, o as IdpAuthorizeRequest, p as WithdrawalBlockedDetails, r as TokenSource, s as IdpTokenRequest, t as ApiError, u as SocialProvider, v as loginRequestSchema, x as tokenPairSchema, y as signupRequestSchema } from "./http-CqAR0_w4.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/analytics-ids.d.ts
|
|
4
4
|
/**
|
|
@@ -489,7 +489,7 @@ export type CreateInquiryRequest = z.infer<typeof createInquiryRequestSchema>;
|
|
|
489
489
|
//#region src/schemas/member.d.ts
|
|
490
490
|
export declare const memberSchema: z.ZodObject<{
|
|
491
491
|
memberId: z.ZodString;
|
|
492
|
-
email: z.ZodEmail
|
|
492
|
+
email: z.ZodNullable<z.ZodEmail>;
|
|
493
493
|
name: z.ZodString;
|
|
494
494
|
phone: z.ZodNullable<z.ZodString>;
|
|
495
495
|
point: z.ZodNumber;
|
|
@@ -672,7 +672,7 @@ export declare function createStorefrontClient(options: StorefrontClientOptions)
|
|
|
672
672
|
/** session.userinfo() 위임 — session 미전달 시 에러 */
|
|
673
673
|
userinfo(): Promise<{
|
|
674
674
|
memberId: string;
|
|
675
|
-
email: string;
|
|
675
|
+
email: string | null;
|
|
676
676
|
name: string;
|
|
677
677
|
phone: string | null;
|
|
678
678
|
point: number;
|
|
@@ -701,6 +701,16 @@ export declare function createStorefrontClient(options: StorefrontClientOptions)
|
|
|
701
701
|
expiresIn: number;
|
|
702
702
|
}>;
|
|
703
703
|
logout: () => Promise<void>;
|
|
704
|
+
/** 소셜 로그인 시작 — 공급자 로그인 주소를 받는다. PKCE·state는 `@sayren/storefront-sdk/auth`의 `idp()`가 만든다 */
|
|
705
|
+
idpAuthorize: (provider: SocialProvider, body: IdpAuthorizeRequest) => Promise<{
|
|
706
|
+
url: string;
|
|
707
|
+
}>;
|
|
708
|
+
/** 소셜 로그인 마무리 — 돌아온 1회용 code + codeVerifier를 구매자 토큰으로 바꾼다 */
|
|
709
|
+
idpToken: (body: IdpTokenRequest) => Promise<{
|
|
710
|
+
accessToken: string;
|
|
711
|
+
refreshToken: string;
|
|
712
|
+
expiresIn: number;
|
|
713
|
+
}>;
|
|
704
714
|
/** 구매자 탈퇴 — 로그인 상태(accessToken)에서 비밀번호로 본인 확인. 성공하면 토큰은 모두 무효 */
|
|
705
715
|
withdraw: (body: WithdrawRequest) => Promise<void>;
|
|
706
716
|
};
|
|
@@ -713,6 +723,7 @@ export declare function createStorefrontClient(options: StorefrontClientOptions)
|
|
|
713
723
|
customerCenterPhone: string | null;
|
|
714
724
|
businessHours: string | null;
|
|
715
725
|
guestCheckout: boolean;
|
|
726
|
+
loginMethods: ("password" | "kakao" | "naver" | "google")[];
|
|
716
727
|
}>;
|
|
717
728
|
};
|
|
718
729
|
catalog: {
|
|
@@ -1140,7 +1151,7 @@ export declare function createStorefrontClient(options: StorefrontClientOptions)
|
|
|
1140
1151
|
member: {
|
|
1141
1152
|
me: () => Promise<{
|
|
1142
1153
|
memberId: string;
|
|
1143
|
-
email: string;
|
|
1154
|
+
email: string | null;
|
|
1144
1155
|
name: string;
|
|
1145
1156
|
phone: string | null;
|
|
1146
1157
|
point: number;
|
|
@@ -1507,7 +1518,13 @@ export declare const storefrontStoreSchema: z.ZodObject<{
|
|
|
1507
1518
|
customerCenterPhone: z.ZodNullable<z.ZodString>;
|
|
1508
1519
|
businessHours: z.ZodNullable<z.ZodString>;
|
|
1509
1520
|
guestCheckout: z.ZodBoolean;
|
|
1521
|
+
loginMethods: z.ZodArray<z.ZodEnum<{
|
|
1522
|
+
password: "password";
|
|
1523
|
+
kakao: "kakao";
|
|
1524
|
+
naver: "naver";
|
|
1525
|
+
google: "google";
|
|
1526
|
+
}>>;
|
|
1510
1527
|
}, z.core.$strip>;
|
|
1511
1528
|
export type StorefrontStore = z.infer<typeof storefrontStoreSchema>;
|
|
1512
1529
|
//#endregion
|
|
1513
|
-
export { AnonymousSession, ApiError, LoginRequest, type Query, type SessionStorage, SignupRequest, type StorefrontClient, type StorefrontClientOptions, type StorefrontSession, type StorefrontSessionOptions, TokenPair, type TokenSource, WithdrawRequest, WithdrawalBlockedDetails, anonymousSessionSchema, buildQuery, loginRequestSchema, signupRequestSchema, tokenPairSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema };
|
|
1530
|
+
export { AnonymousSession, ApiError, IdpAuthorizeRequest, IdpTokenRequest, LoginRequest, type Query, type SessionStorage, SignupRequest, SocialProvider, type StorefrontClient, type StorefrontClientOptions, type StorefrontSession, type StorefrontSessionOptions, TokenPair, type TokenSource, WithdrawRequest, WithdrawalBlockedDetails, anonymousSessionSchema, buildQuery, idpAuthorizeRequestSchema, idpAuthorizeResponseSchema, idpTokenRequestSchema, loginRequestSchema, signupRequestSchema, socialProviderSchema, tokenPairSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as ANALYTICS_VISITOR_HEADER, c as analyticsClientEventSchema, i as ANALYTICS_VISITOR_COOKIE, l as analyticsIdSchema, n as ANALYTICS_SESSION_COOKIE, o as analyticsBatchSchema, r as ANALYTICS_SESSION_HEADER, s as analyticsClientEventNameSchema, t as ANALYTICS_MAX_BATCH_BYTES, u as analyticsServerEventNameSchema } from "./analytics-CgD70OqH.mjs";
|
|
2
2
|
import { t as analyticsIdsFromCookie } from "./analytics-ids-COElZiI3.mjs";
|
|
3
|
-
import { $ as
|
|
3
|
+
import { $ as idpAuthorizeRequestSchema, A as pageSchema, B as pgParamsSchema, C as claimTypeSchema, D as claimStatusSchema, E as myClaimSchema, F as createCheckoutRequestSchema, G as productDetailSchema, H as shippingAddressInputSchema, I as guestInfoSchema, J as addCartItemRequestSchema, K as productFacetsSchema, L as paymentMethodSchema, M as checkoutSessionSchema, N as confirmPaymentRequestSchema, O as orderItemStatusSchema, P as confirmPaymentResultSchema, Q as anonymousSessionSchema, R as paymentParamsSchema, S as claimReasonSchema, T as createClaimResultSchema, U as categoryNodeSchema, V as requestPaymentRequestSchema, W as productCardSchema, X as cartSchema, Y as cartItemSchema, Z as updateCartItemRequestSchema, _ as createInquiryRequestSchema, a as publicReviewSchema, at as tokenPairSchema, b as customerInquiryCategorySchema, c as writableReviewSchema, ct as ApiError, d as myOrderSchema, dt as unwrapData, et as idpAuthorizeResponseSchema, f as orderShippingAddressSchema, g as updateProfileRequestSchema, h as memberSchema, i as createReviewResultSchema, it as socialProviderSchema, j as PAYMENT_STATUS_VALUES, k as pageInfoSchema, l as deliveryTrackingSchema, lt as buildQuery, m as memberAddressSchema, n as storefrontStoreSchema, nt as loginRequestSchema, o as reviewSummarySchema, ot as withdrawRequestSchema, p as memberAddressRequestSchema, q as productSortSchema, r as createReviewRequestSchema, rt as signupRequestSchema, s as updateReviewRequestSchema, st as withdrawalBlockedDetailsSchema, t as createStorefrontClient, tt as idpTokenRequestSchema, u as myOrderItemSchema, ut as toApiError, v as publicInquirySchema, w as createClaimRequestSchema, x as customerInquirySchema, y as createCustomerInquiryRequestSchema, z as paymentStatusSchema } from "./client-Do9-LG5X.mjs";
|
|
4
4
|
//#region src/session.ts
|
|
5
5
|
/** 세션 키 상수 — SDK가 소유한다. store-sdk와 겹치지 않는 별도 네임스페이스. */
|
|
6
6
|
const STOREFRONT_SESSION_KEYS = {
|
|
@@ -124,4 +124,4 @@ function createStorefrontSession(options) {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
//#endregion
|
|
127
|
-
export { ANALYTICS_MAX_BATCH_BYTES, ANALYTICS_SESSION_COOKIE, ANALYTICS_SESSION_HEADER, ANALYTICS_VISITOR_COOKIE, ANALYTICS_VISITOR_HEADER, ApiError, PAYMENT_STATUS_VALUES, STOREFRONT_SESSION_KEYS, addCartItemRequestSchema, analyticsBatchSchema, analyticsClientEventNameSchema, analyticsClientEventSchema, analyticsIdSchema, analyticsIdsFromCookie, analyticsServerEventNameSchema, anonymousSessionSchema, buildQuery, cartItemSchema, cartSchema, categoryNodeSchema, checkoutSessionSchema, claimReasonSchema, claimStatusSchema, claimTypeSchema, confirmPaymentRequestSchema, confirmPaymentResultSchema, createCheckoutRequestSchema, createClaimRequestSchema, createClaimResultSchema, createCustomerInquiryRequestSchema, createInquiryRequestSchema, createReviewRequestSchema, createReviewResultSchema, createStorefrontClient, createStorefrontSession, customerInquiryCategorySchema, customerInquirySchema, deliveryTrackingSchema, guestInfoSchema, loginRequestSchema, memberAddressRequestSchema, memberAddressSchema, memberSchema, myClaimSchema, myOrderItemSchema, myOrderSchema, orderItemStatusSchema, orderShippingAddressSchema, pageInfoSchema, pageSchema, paymentMethodSchema, paymentParamsSchema, paymentStatusSchema, pgParamsSchema, productCardSchema, productDetailSchema, productFacetsSchema, productSortSchema, publicInquirySchema, publicReviewSchema, requestPaymentRequestSchema, reviewSummarySchema, shippingAddressInputSchema, signupRequestSchema, storefrontStoreSchema, tokenPairSchema, updateCartItemRequestSchema, updateProfileRequestSchema, updateReviewRequestSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema, writableReviewSchema };
|
|
127
|
+
export { ANALYTICS_MAX_BATCH_BYTES, ANALYTICS_SESSION_COOKIE, ANALYTICS_SESSION_HEADER, ANALYTICS_VISITOR_COOKIE, ANALYTICS_VISITOR_HEADER, ApiError, PAYMENT_STATUS_VALUES, STOREFRONT_SESSION_KEYS, addCartItemRequestSchema, analyticsBatchSchema, analyticsClientEventNameSchema, analyticsClientEventSchema, analyticsIdSchema, analyticsIdsFromCookie, analyticsServerEventNameSchema, anonymousSessionSchema, buildQuery, cartItemSchema, cartSchema, categoryNodeSchema, checkoutSessionSchema, claimReasonSchema, claimStatusSchema, claimTypeSchema, confirmPaymentRequestSchema, confirmPaymentResultSchema, createCheckoutRequestSchema, createClaimRequestSchema, createClaimResultSchema, createCustomerInquiryRequestSchema, createInquiryRequestSchema, createReviewRequestSchema, createReviewResultSchema, createStorefrontClient, createStorefrontSession, customerInquiryCategorySchema, customerInquirySchema, deliveryTrackingSchema, guestInfoSchema, idpAuthorizeRequestSchema, idpAuthorizeResponseSchema, idpTokenRequestSchema, loginRequestSchema, memberAddressRequestSchema, memberAddressSchema, memberSchema, myClaimSchema, myOrderItemSchema, myOrderSchema, orderItemStatusSchema, orderShippingAddressSchema, pageInfoSchema, pageSchema, paymentMethodSchema, paymentParamsSchema, paymentStatusSchema, pgParamsSchema, productCardSchema, productDetailSchema, productFacetsSchema, productSortSchema, publicInquirySchema, publicReviewSchema, requestPaymentRequestSchema, reviewSummarySchema, shippingAddressInputSchema, signupRequestSchema, socialProviderSchema, storefrontStoreSchema, tokenPairSchema, updateCartItemRequestSchema, updateProfileRequestSchema, updateReviewRequestSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema, writableReviewSchema };
|
package/package.json
CHANGED
package/src/auth/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
AnonymousSession,
|
|
5
5
|
LoginRequest,
|
|
6
6
|
SignupRequest,
|
|
7
|
+
SocialProvider,
|
|
7
8
|
TokenPair,
|
|
8
9
|
WithdrawRequest,
|
|
9
10
|
} from "../schemas/auth";
|
|
@@ -14,11 +15,60 @@ export type {
|
|
|
14
15
|
AnonymousSession,
|
|
15
16
|
LoginRequest,
|
|
16
17
|
SignupRequest,
|
|
18
|
+
SocialProvider,
|
|
17
19
|
TokenPair,
|
|
18
20
|
WithdrawalBlockedDetails,
|
|
19
21
|
WithdrawRequest,
|
|
20
22
|
} from "../schemas/auth";
|
|
21
23
|
|
|
24
|
+
/** 소셜 로그인 시작 결과 — `codeVerifier`와 `state`는 돌아올 때까지 서버 세션(httpOnly 쿠키 등)에 보관한다 */
|
|
25
|
+
export interface IdpStart {
|
|
26
|
+
/** 구매자를 보낼 공급자 로그인 주소 */
|
|
27
|
+
url: string;
|
|
28
|
+
state: string;
|
|
29
|
+
codeVerifier: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IdpCallbackInput {
|
|
33
|
+
/** 돌아온 주소의 `code`(성공) — 실패면 `error`가 온다 */
|
|
34
|
+
code: string | null;
|
|
35
|
+
/** 돌아온 주소의 `state` */
|
|
36
|
+
state: string | null;
|
|
37
|
+
/** `idp()`가 준 값 — 보관해 둔 것 */
|
|
38
|
+
expectedState: string;
|
|
39
|
+
codeVerifier: string;
|
|
40
|
+
/** 비회원 장바구니 토큰 — 주면 회원 장바구니로 합친다 */
|
|
41
|
+
cartToken?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 소셜 로그인 실패 — 구매자가 취소했거나(`access_denied`) state가 맞지 않는다 */
|
|
45
|
+
export class IdpCallbackError extends Error {
|
|
46
|
+
constructor(
|
|
47
|
+
public readonly reason: "access_denied" | "state_mismatch" | "missing_code" | string,
|
|
48
|
+
) {
|
|
49
|
+
super(`소셜 로그인을 마치지 못했어요 (${reason})`);
|
|
50
|
+
this.name = "IdpCallbackError";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function base64url(bytes: Uint8Array): string {
|
|
55
|
+
let binary = "";
|
|
56
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
57
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function randomToken(bytes = 32): string {
|
|
61
|
+
return base64url(globalThis.crypto.getRandomValues(new Uint8Array(bytes)));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function s256(verifier: string): Promise<string> {
|
|
65
|
+
const digest = await globalThis.crypto.subtle.digest(
|
|
66
|
+
"SHA-256",
|
|
67
|
+
new TextEncoder().encode(verifier),
|
|
68
|
+
);
|
|
69
|
+
return base64url(new Uint8Array(digest));
|
|
70
|
+
}
|
|
71
|
+
|
|
22
72
|
export interface StorefrontAuthOptions {
|
|
23
73
|
/** 예: https://api.sayren.app/storefront/v1 */
|
|
24
74
|
baseUrl: string;
|
|
@@ -65,6 +115,37 @@ export function createStorefrontAuth(options: StorefrontAuthOptions) {
|
|
|
65
115
|
signUp(body: SignupRequest): Promise<TokenPair> {
|
|
66
116
|
return client().auth.signup(body);
|
|
67
117
|
},
|
|
118
|
+
/**
|
|
119
|
+
* 소셜 로그인 시작 — PKCE(S256)와 state를 만들고 공급자 로그인 주소를 받는다. 반환한 `state`·`codeVerifier`를
|
|
120
|
+
* 서버 세션에 보관하고 구매자를 `url`로 보낸다. 로그인을 마치면 `redirectUri`로 `?code=&state=`가 붙어 돌아온다.
|
|
121
|
+
* `redirectUri`는 스토어프런트 도메인 목록 안이어야 한다
|
|
122
|
+
*/
|
|
123
|
+
async idp(provider: SocialProvider, options: { redirectUri: string }): Promise<IdpStart> {
|
|
124
|
+
const state = randomToken(24);
|
|
125
|
+
const codeVerifier = randomToken(32);
|
|
126
|
+
const { url } = await client().auth.idpAuthorize(provider, {
|
|
127
|
+
redirectUri: options.redirectUri,
|
|
128
|
+
codeChallenge: await s256(codeVerifier),
|
|
129
|
+
state,
|
|
130
|
+
});
|
|
131
|
+
return { url, state, codeVerifier };
|
|
132
|
+
},
|
|
133
|
+
/**
|
|
134
|
+
* 소셜 로그인 마무리 — 돌아온 `code`를 구매자 토큰으로 바꾼다. state가 다르거나 구매자가 취소했으면
|
|
135
|
+
* `IdpCallbackError`. code는 1회용이고 1분 안에 바꿔야 한다
|
|
136
|
+
*/
|
|
137
|
+
idpCallback(input: IdpCallbackInput & { error?: string | null }): Promise<TokenPair> {
|
|
138
|
+
if (input.error) return Promise.reject(new IdpCallbackError(input.error));
|
|
139
|
+
if (!input.state || input.state !== input.expectedState) {
|
|
140
|
+
return Promise.reject(new IdpCallbackError("state_mismatch"));
|
|
141
|
+
}
|
|
142
|
+
if (!input.code) return Promise.reject(new IdpCallbackError("missing_code"));
|
|
143
|
+
return client().auth.idpToken({
|
|
144
|
+
code: input.code,
|
|
145
|
+
codeVerifier: input.codeVerifier,
|
|
146
|
+
cartToken: input.cartToken,
|
|
147
|
+
});
|
|
148
|
+
},
|
|
68
149
|
/** 토큰 갱신 — refreshToken은 한 번 쓰면 폐기되고 새 쌍이 온다(회전) */
|
|
69
150
|
refresh(refreshToken: string): Promise<TokenPair> {
|
|
70
151
|
return client().auth.refresh(refreshToken);
|
package/src/client.ts
CHANGED
|
@@ -4,7 +4,13 @@ import { ANALYTICS_SESSION_HEADER, ANALYTICS_VISITOR_HEADER } from "./schemas/an
|
|
|
4
4
|
import type { LoginRequest, SignupRequest } from "./schemas/auth";
|
|
5
5
|
import {
|
|
6
6
|
anonymousSessionSchema,
|
|
7
|
+
type IdpAuthorizeRequest,
|
|
8
|
+
type IdpTokenRequest,
|
|
9
|
+
idpAuthorizeRequestSchema,
|
|
10
|
+
idpAuthorizeResponseSchema,
|
|
11
|
+
idpTokenRequestSchema,
|
|
7
12
|
loginRequestSchema,
|
|
13
|
+
type SocialProvider,
|
|
8
14
|
signupRequestSchema,
|
|
9
15
|
tokenPairSchema,
|
|
10
16
|
type WithdrawRequest,
|
|
@@ -184,6 +190,16 @@ export function createStorefrontClient(options: StorefrontClientOptions) {
|
|
|
184
190
|
refresh: (refreshToken: string) =>
|
|
185
191
|
http.request("POST", "/auth/token/refresh", tokenPairSchema, { body: { refreshToken } }),
|
|
186
192
|
logout: () => http.requestVoid("POST", "/auth/logout"),
|
|
193
|
+
/** 소셜 로그인 시작 — 공급자 로그인 주소를 받는다. PKCE·state는 `@sayren/storefront-sdk/auth`의 `idp()`가 만든다 */
|
|
194
|
+
idpAuthorize: (provider: SocialProvider, body: IdpAuthorizeRequest) =>
|
|
195
|
+
http.request("POST", path`/auth/idp/${provider}/authorize`, idpAuthorizeResponseSchema, {
|
|
196
|
+
body: idpAuthorizeRequestSchema.parse(body),
|
|
197
|
+
}),
|
|
198
|
+
/** 소셜 로그인 마무리 — 돌아온 1회용 code + codeVerifier를 구매자 토큰으로 바꾼다 */
|
|
199
|
+
idpToken: (body: IdpTokenRequest) =>
|
|
200
|
+
http.request("POST", "/auth/idp/token", tokenPairSchema, {
|
|
201
|
+
body: idpTokenRequestSchema.parse(body),
|
|
202
|
+
}),
|
|
187
203
|
/** 구매자 탈퇴 — 로그인 상태(accessToken)에서 비밀번호로 본인 확인. 성공하면 토큰은 모두 무효 */
|
|
188
204
|
withdraw: (body: WithdrawRequest) =>
|
|
189
205
|
http.requestVoid("POST", "/auth/withdraw", { body: withdrawRequestSchema.parse(body) }),
|
package/src/schemas/auth.ts
CHANGED
|
@@ -61,7 +61,11 @@ export type AnonymousSession = z.infer<typeof anonymousSessionSchema>;
|
|
|
61
61
|
* 403 `PASSWORD_MISMATCH`다(로그인과 같은 5회 잠금).
|
|
62
62
|
*/
|
|
63
63
|
export const withdrawRequestSchema = z.object({
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
|
+
* 비밀번호로 가입한 구매자는 필수. 소셜 로그인으로만 가입한 구매자는 비밀번호가 없어 생략하고, 대신 최근 10분 안에
|
|
66
|
+
* 로그인한 액세스 토큰이어야 한다(아니면 403 `REAUTH_REQUIRED` — 다시 로그인한 뒤 호출)
|
|
67
|
+
*/
|
|
68
|
+
password: z.string().min(1).optional(),
|
|
65
69
|
});
|
|
66
70
|
|
|
67
71
|
export type WithdrawRequest = z.infer<typeof withdrawRequestSchema>;
|
|
@@ -77,3 +81,32 @@ export const withdrawalBlockedDetailsSchema = z.object({
|
|
|
77
81
|
});
|
|
78
82
|
|
|
79
83
|
export type WithdrawalBlockedDetails = z.infer<typeof withdrawalBlockedDetailsSchema>;
|
|
84
|
+
|
|
85
|
+
/** 소셜 로그인 공급자 */
|
|
86
|
+
export const socialProviderSchema = z.enum(["kakao", "naver", "google"]);
|
|
87
|
+
export type SocialProvider = z.infer<typeof socialProviderSchema>;
|
|
88
|
+
|
|
89
|
+
/** `POST /auth/idp/{provider}/authorize` 본문 — PKCE(S256)와 state는 SDK가 만든다 */
|
|
90
|
+
export const idpAuthorizeRequestSchema = z.object({
|
|
91
|
+
/** 로그인을 마치고 돌아올 스토어프런트 주소. 스토어프런트 도메인 목록 안이어야 한다 */
|
|
92
|
+
redirectUri: z.url(),
|
|
93
|
+
/** base64url(SHA-256(codeVerifier)) */
|
|
94
|
+
codeChallenge: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
95
|
+
/** 스토어프런트가 만든 값 — 돌아올 때 그대로 붙어 온다. 세션에 저장해 두고 비교한다 */
|
|
96
|
+
state: z.string().min(16).max(128),
|
|
97
|
+
});
|
|
98
|
+
export type IdpAuthorizeRequest = z.infer<typeof idpAuthorizeRequestSchema>;
|
|
99
|
+
|
|
100
|
+
export const idpAuthorizeResponseSchema = z.object({
|
|
101
|
+
/** 구매자를 보낼 공급자 로그인 주소 */
|
|
102
|
+
url: z.url(),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/** `POST /auth/idp/token` 본문 — 돌아온 1회용 code를 구매자 토큰으로 바꾼다 */
|
|
106
|
+
export const idpTokenRequestSchema = z.object({
|
|
107
|
+
code: z.string().min(1),
|
|
108
|
+
codeVerifier: z.string().regex(/^[A-Za-z0-9._~-]{43,128}$/),
|
|
109
|
+
/** 비회원 장바구니 토큰 — 주면 회원 장바구니로 합친다 */
|
|
110
|
+
cartToken: z.string().optional(),
|
|
111
|
+
});
|
|
112
|
+
export type IdpTokenRequest = z.infer<typeof idpTokenRequestSchema>;
|
package/src/schemas/member.ts
CHANGED
package/src/schemas/store.ts
CHANGED
|
@@ -14,6 +14,10 @@ export const storefrontStoreSchema = z.object({
|
|
|
14
14
|
* 장바구니 담기는 된다 — 주문 버튼을 로그인 안내로 바꾸면 된다
|
|
15
15
|
*/
|
|
16
16
|
guestCheckout: z.boolean(),
|
|
17
|
+
/**
|
|
18
|
+
* 켜진 로그인 방법 — 화면에 이 순서대로 버튼을 그리면 된다. `password`가 없으면 이메일·비밀번호 가입·로그인이 꺼져 있다
|
|
19
|
+
*/
|
|
20
|
+
loginMethods: z.array(z.enum(["password", "kakao", "naver", "google"])),
|
|
17
21
|
});
|
|
18
22
|
|
|
19
23
|
export type StorefrontStore = z.infer<typeof storefrontStoreSchema>;
|