medusa-google-login-logic 1.0.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/README.md +126 -0
- package/dist/google-login-logic/src/hooks/useGoogleAuth.d.ts +18 -0
- package/dist/google-login-logic/src/index.d.ts +2 -0
- package/dist/medusa-services/contact-action.d.ts +9 -0
- package/dist/medusa-services/google-auth.d.ts +21 -0
- package/dist/medusa-services/google-login.d.ts +36 -0
- package/dist/ui-library.js +164 -0
- package/dist/ui-library.umd.cjs +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Medusa Google Login Logic
|
|
2
|
+
|
|
3
|
+
The `medusa-google-login-logic` is a brand new library designed to handle Google OAuth authentication for MedusaJS applications. It is built to be modular, robust, and mirrors production-tested logic (ported from `B&B_ui`).
|
|
4
|
+
|
|
5
|
+
## 🏗 Architecture
|
|
6
|
+
|
|
7
|
+
The library is split into two layers:
|
|
8
|
+
|
|
9
|
+
1. **Service Layer (`medusa-services/google-login.ts`)**: Contains pure TypeScript functions for interacting with Medusa API and handling OAuth URL logic.
|
|
10
|
+
2. **Hook Layer (`google-login-logic/src/hooks/useGoogleAuth.ts`)**: A React hook that orchestrates the flow, manages state (loading, error), and handles UI events.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 🛠 Core Logic Explanation
|
|
15
|
+
|
|
16
|
+
### 1. The Login Flow (`login` function)
|
|
17
|
+
When you call the `login()` function from the hook:
|
|
18
|
+
- **Base URL Detection**: It automatically detects your storefront URL using environment variables (`NEXT_PUBLIC_BASE_URL` or `NEXT_PUBLIC_MEDUSA_STOREFRONT_URL`) or `window.location.origin`.
|
|
19
|
+
- **Production Routing**: If it detects it's not on `localhost` and matches the `productionDomain` (default: `bellyandbaby.co`), it forces the use of the production URL.
|
|
20
|
+
- **URI Normalization**: It ensures the `redirect_uri` sent to Google matches the Google Console exactly (handles trailing slashes and protocols).
|
|
21
|
+
- **Redirection**: It calls the Medusa SDK to get the Google login location and redirects the user.
|
|
22
|
+
|
|
23
|
+
### 2. The Callback Flow (`processCallback` function)
|
|
24
|
+
The hook automatically detects the `code` in the URL (if `queryParams` are passed) and completes the login:
|
|
25
|
+
- **Token Exchange**: Exchanges the Google authorization code for a Medusa authentication token.
|
|
26
|
+
- **Account Linking Check**:
|
|
27
|
+
- It decodes the token to check for `actor_id`.
|
|
28
|
+
- If `actor_id` is missing, it means the Google account is not yet linked to a Medusa customer.
|
|
29
|
+
- **Automatic Customer Creation**:
|
|
30
|
+
- For new users, it automatically calls `createCustomer` using the email extracted from the Google token.
|
|
31
|
+
- If the user already has an account but it's not linked, it handles the conflict gracefully and asks for a re-login (`onNeedsReLogin`).
|
|
32
|
+
- **Session Management**: Automatically refreshes the token for existing customers to ensure a valid session and retrieves full customer data.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 🚀 How to Use
|
|
37
|
+
|
|
38
|
+
### 1. In your Login Button
|
|
39
|
+
```tsx
|
|
40
|
+
import { useGoogleAuth } from 'medusa-google-login-logic';
|
|
41
|
+
|
|
42
|
+
const GoogleLoginButton = ({ sdk }) => {
|
|
43
|
+
const { login, isLoading, error } = useGoogleAuth({ sdk });
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<button onClick={login} disabled={isLoading}>
|
|
47
|
+
{isLoading ? 'Redirecting...' : 'Continue with Google'}
|
|
48
|
+
</button>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. In your Callback Page
|
|
54
|
+
```tsx
|
|
55
|
+
import { useGoogleAuth } from 'medusa-google-login-logic';
|
|
56
|
+
import { useSearchParams, useRouter } from 'next/navigation';
|
|
57
|
+
|
|
58
|
+
const GoogleCallbackPage = ({ sdk }) => {
|
|
59
|
+
const searchParams = useSearchParams();
|
|
60
|
+
const queryParams = Object.fromEntries(searchParams.entries());
|
|
61
|
+
const router = useRouter();
|
|
62
|
+
|
|
63
|
+
const { isLoading, error, customer } = useGoogleAuth({
|
|
64
|
+
sdk,
|
|
65
|
+
queryParams, // Hook auto-triggers logic when queryParams.code exists
|
|
66
|
+
onSuccess: (data) => {
|
|
67
|
+
router.push('/account');
|
|
68
|
+
},
|
|
69
|
+
onError: (msg) => alert(msg),
|
|
70
|
+
onNeedsReLogin: (email) => {
|
|
71
|
+
// Typically happens after automatic account creation
|
|
72
|
+
alert("Account created! Please login again with Google.");
|
|
73
|
+
router.push('/login');
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (isLoading) return <div>Authenticating...</div>;
|
|
78
|
+
if (error) return <div>Error: {error}</div>;
|
|
79
|
+
|
|
80
|
+
return <div>Welcome back!</div>;
|
|
81
|
+
};
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## ⚙️ Setup for Other Projects
|
|
85
|
+
|
|
86
|
+
To use this library in a new project, follow these steps:
|
|
87
|
+
|
|
88
|
+
### 1. Installation
|
|
89
|
+
If your project is in the same mono-repo:
|
|
90
|
+
```bash
|
|
91
|
+
npm install ../path/to/medusa-google-login-logic
|
|
92
|
+
```
|
|
93
|
+
Or if published:
|
|
94
|
+
```bash
|
|
95
|
+
npm install medusa-google-login-logic
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 2. Required Environment Variables
|
|
99
|
+
Add these to your project's `.env` or `.env.local`:
|
|
100
|
+
- `NEXT_PUBLIC_MEDUSA_BACKEND_URL`: Your Medusa backend URL.
|
|
101
|
+
- `NEXT_PUBLIC_BASE_URL`: Your storefront URL (e.g., `https://my-store.com`).
|
|
102
|
+
- `NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY`: Your Medusa publishable key.
|
|
103
|
+
|
|
104
|
+
### 3. Google OAuth Console Configuration
|
|
105
|
+
You MUST add your callback URL to the **Authorized redirect URIs** in your [Google Cloud Console](https://console.cloud.google.com/):
|
|
106
|
+
|
|
107
|
+
- **Local Development**: `http://localhost:8000/auth/customer/google/callback`
|
|
108
|
+
- **Production**: `https://yourdomain.com/auth/customer/google/callback`
|
|
109
|
+
|
|
110
|
+
> [!IMPORTANT]
|
|
111
|
+
> Ensure the URL matches exactly (no trailing slash, matching protocol).
|
|
112
|
+
|
|
113
|
+
### 4. Code Implementation
|
|
114
|
+
Pass your configured Medusa SDK instance to the hook. The hook handles the rest.
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import { Medusa } from "@medusajs/js-sdk";
|
|
118
|
+
import { useGoogleAuth } from "medusa-google-login-logic";
|
|
119
|
+
|
|
120
|
+
const sdk = new Medusa({
|
|
121
|
+
baseUrl: process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL,
|
|
122
|
+
publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Use in your components as shown in the examples above!
|
|
126
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface UseGoogleAuthProps {
|
|
2
|
+
sdk: any;
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
productionDomain?: string;
|
|
5
|
+
callbackPath?: string;
|
|
6
|
+
queryParams?: Record<string, string>;
|
|
7
|
+
onSuccess?: (customer: any) => void;
|
|
8
|
+
onError?: (error: string) => void;
|
|
9
|
+
onNeedsReLogin?: (email: string) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare const useGoogleAuth: ({ sdk, baseUrl: propsBaseUrl, productionDomain, callbackPath, queryParams, onSuccess, onError, onNeedsReLogin }: UseGoogleAuthProps) => {
|
|
12
|
+
login: () => Promise<void>;
|
|
13
|
+
isLoading: boolean;
|
|
14
|
+
error: string | null;
|
|
15
|
+
customer: any;
|
|
16
|
+
needsReLogin: boolean;
|
|
17
|
+
processCallback: () => Promise<void>;
|
|
18
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface ContactSubmissionData {
|
|
2
|
+
backendUrl: string;
|
|
3
|
+
publishableKey?: string;
|
|
4
|
+
data: Record<string, any>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Server-side compatible function to send contact request
|
|
8
|
+
*/
|
|
9
|
+
export declare function sendContactRequest({ backendUrl, publishableKey, data }: ContactSubmissionData): Promise<any>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes and validates redirect URI to match Google OAuth console exactly
|
|
3
|
+
*/
|
|
4
|
+
export declare function normalizeRedirectUri(url: string, isLocalhost: boolean): string;
|
|
5
|
+
export declare const GOOGLE_CALLBACK_PATH = "/auth/customer/google/callback";
|
|
6
|
+
/**
|
|
7
|
+
* Validates redirect URI format matches expected Google OAuth console configuration
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateRedirectUri(uri: string, isLocalhost: boolean, productionDomain: string): {
|
|
10
|
+
valid: boolean;
|
|
11
|
+
expected: string;
|
|
12
|
+
actual: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Core API logic to initiate Google login
|
|
16
|
+
*/
|
|
17
|
+
export declare function performGoogleLogin(sdk: any, redirectUri: string): Promise<any>;
|
|
18
|
+
/**
|
|
19
|
+
* Decodes a JWT token safely
|
|
20
|
+
*/
|
|
21
|
+
export declare function decodeToken(token: string): any;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ported from B&B_ui/src/modules/account/components/google-login-button/actions.ts
|
|
3
|
+
* and B&B_ui/src/app/auth/google/callback/actions.ts
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Normalizes and validates redirect URI to match Google OAuth console exactly
|
|
7
|
+
*/
|
|
8
|
+
export declare function normalizeRedirectUri(url: string, isLocalhost: boolean): string;
|
|
9
|
+
/**
|
|
10
|
+
* Validates redirect URI format matches expected Google OAuth console configuration
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateRedirectUri(uri: string, isLocalhost: boolean, productionDomain: string): {
|
|
13
|
+
valid: boolean;
|
|
14
|
+
expected: string;
|
|
15
|
+
actual: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Initiates Google login
|
|
19
|
+
*/
|
|
20
|
+
export declare function performGoogleLogin(sdk: any, redirectUri: string): Promise<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Decodes a JWT token safely
|
|
23
|
+
*/
|
|
24
|
+
export declare function decodeToken(token: string | null | undefined): any;
|
|
25
|
+
/**
|
|
26
|
+
* Refreshes auth token
|
|
27
|
+
*/
|
|
28
|
+
export declare function refreshToken(sdk: any): Promise<string | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Retrieves current customer data
|
|
31
|
+
*/
|
|
32
|
+
export declare function retrieveCustomer(sdk: any, token?: string): Promise<any>;
|
|
33
|
+
/**
|
|
34
|
+
* Creates a new customer
|
|
35
|
+
*/
|
|
36
|
+
export declare function createCustomer(sdk: any, email: string, token: string): Promise<any>;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { useState as w, useCallback as k, useEffect as z } from "react";
|
|
2
|
+
function C(t, r) {
|
|
3
|
+
try {
|
|
4
|
+
const e = new URL(t);
|
|
5
|
+
return e.pathname = e.pathname.replace(/\/$/, ""), r ? e.protocol = "http:" : e.protocol = "https:", e.toString().replace(/\/$/, "");
|
|
6
|
+
} catch (e) {
|
|
7
|
+
return console.warn("Failed to normalize redirect URI:", e), t.replace(/\/$/, "");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function M(t, r, e) {
|
|
11
|
+
const o = r ? "http://localhost:8000/auth/customer/google/callback" : `https://${e}/auth/customer/google/callback`, a = C(t, r), f = a === o;
|
|
12
|
+
return f || console.warn("⚠️ Redirect URI validation mismatch:", {
|
|
13
|
+
expected: o,
|
|
14
|
+
actual: a,
|
|
15
|
+
difference: a.replace(o, "") || o.replace(a, "")
|
|
16
|
+
}), {
|
|
17
|
+
valid: f,
|
|
18
|
+
expected: o,
|
|
19
|
+
actual: a
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async function A(t, r) {
|
|
23
|
+
try {
|
|
24
|
+
return await t.auth.login("customer", "google", {
|
|
25
|
+
redirect_uri: r
|
|
26
|
+
});
|
|
27
|
+
} catch (e) {
|
|
28
|
+
throw console.error("❌ Google login initiation failed:", e), e;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function R(t) {
|
|
32
|
+
if (!t) return null;
|
|
33
|
+
try {
|
|
34
|
+
const r = t.split(".");
|
|
35
|
+
if (r.length !== 3) return null;
|
|
36
|
+
const e = r[1], o = e + "=".repeat((4 - e.length % 4) % 4), a = typeof Buffer < "u" ? Buffer.from(o.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf-8") : atob(o.replace(/-/g, "+").replace(/_/g, "/"));
|
|
37
|
+
return JSON.parse(a);
|
|
38
|
+
} catch (r) {
|
|
39
|
+
return console.error("Failed to decode token:", r), null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function S(t) {
|
|
43
|
+
try {
|
|
44
|
+
return await t.auth.refresh();
|
|
45
|
+
} catch (r) {
|
|
46
|
+
return console.error("❌ Token refresh failed:", r), null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function x(t, r) {
|
|
50
|
+
try {
|
|
51
|
+
const e = {};
|
|
52
|
+
return r && (e.authorization = `Bearer ${r}`), (await t.client.fetch("/store/customers/me", {
|
|
53
|
+
method: "GET",
|
|
54
|
+
query: {
|
|
55
|
+
fields: "*orders"
|
|
56
|
+
},
|
|
57
|
+
headers: e
|
|
58
|
+
})).customer;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
throw console.error("❌ Customer retrieval failed:", e), e;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function E(t, r, e) {
|
|
64
|
+
try {
|
|
65
|
+
return (await t.store.customer.create(
|
|
66
|
+
{ email: r },
|
|
67
|
+
{},
|
|
68
|
+
{ authorization: `Bearer ${e}` }
|
|
69
|
+
)).customer;
|
|
70
|
+
} catch (o) {
|
|
71
|
+
throw console.error("❌ Customer creation failed:", o), o;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const y = { BASE_URL: "/", DEV: !1, MODE: "production", PROD: !0, SSR: !1 }, V = ({
|
|
75
|
+
sdk: t,
|
|
76
|
+
baseUrl: r,
|
|
77
|
+
productionDomain: e = "bellyandbaby.co",
|
|
78
|
+
callbackPath: o = "/auth/customer/google/callback",
|
|
79
|
+
queryParams: a,
|
|
80
|
+
onSuccess: f,
|
|
81
|
+
onError: u,
|
|
82
|
+
onNeedsReLogin: h
|
|
83
|
+
}) => {
|
|
84
|
+
const [$, d] = w(!1), [B, p] = w(null), [I, v] = w(null), [G, b] = w(!1), U = (c) => {
|
|
85
|
+
var n;
|
|
86
|
+
try {
|
|
87
|
+
return ((n = process.env) == null ? void 0 : n[c]) || (y == null ? void 0 : y[c]);
|
|
88
|
+
} catch {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}, O = k(async () => {
|
|
92
|
+
d(!0), p(null);
|
|
93
|
+
try {
|
|
94
|
+
const c = r || U("NEXT_PUBLIC_MEDUSA_STOREFRONT_URL") || U("NEXT_PUBLIC_BASE_URL") || (typeof window < "u" ? window.location.origin : ""), n = c.includes("localhost") || c.includes("127.0.0.1");
|
|
95
|
+
let i = c;
|
|
96
|
+
!n && e && c.includes(e) && (i = `https://${e}`);
|
|
97
|
+
const l = C(`${i}${o}`, n), s = await A(t, l);
|
|
98
|
+
if (typeof s == "object" && (s != null && s.location)) {
|
|
99
|
+
window.location.href = s.location;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (typeof s != "string")
|
|
103
|
+
throw new Error("Unexpected authentication result");
|
|
104
|
+
window.location.reload();
|
|
105
|
+
} catch (c) {
|
|
106
|
+
console.error("Google login initiation failed:", c);
|
|
107
|
+
const n = c.message || "Failed to initiate Google login";
|
|
108
|
+
p(n), u == null || u(n), d(!1);
|
|
109
|
+
}
|
|
110
|
+
}, [t, r, e, o, u]), m = k(async () => {
|
|
111
|
+
var c, n;
|
|
112
|
+
if (!(!a || !a.code)) {
|
|
113
|
+
d(!0), p(null);
|
|
114
|
+
try {
|
|
115
|
+
const i = await t.auth.callback("customer", "google", a);
|
|
116
|
+
if (typeof i != "string")
|
|
117
|
+
throw new Error("Invalid token response");
|
|
118
|
+
const l = R(i), s = (c = l == null ? void 0 : l.user_metadata) == null ? void 0 : c.email;
|
|
119
|
+
if (!(l == null ? void 0 : l.actor_id)) {
|
|
120
|
+
if (!s)
|
|
121
|
+
throw new Error("No email found in token");
|
|
122
|
+
try {
|
|
123
|
+
await E(t, s, i), b(!0), h == null || h(s), d(!1);
|
|
124
|
+
return;
|
|
125
|
+
} catch (g) {
|
|
126
|
+
if (g.status === 422 || g.status === 409 || (n = g.message) != null && n.includes("already exists")) {
|
|
127
|
+
b(!0), h == null || h(s), d(!1);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
throw g;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const _ = await S(t) || i, T = await x(t, _);
|
|
134
|
+
v(T), f == null || f(T);
|
|
135
|
+
} catch (i) {
|
|
136
|
+
console.error("Google callback processing failed:", i);
|
|
137
|
+
const l = i.message || "Authentication failed";
|
|
138
|
+
p(l), u == null || u(l);
|
|
139
|
+
} finally {
|
|
140
|
+
d(!1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}, [t, a, f, u, h]);
|
|
144
|
+
return z(() => {
|
|
145
|
+
a && a.code && m();
|
|
146
|
+
}, [a, m]), {
|
|
147
|
+
login: O,
|
|
148
|
+
isLoading: $,
|
|
149
|
+
error: B,
|
|
150
|
+
customer: I,
|
|
151
|
+
needsReLogin: G,
|
|
152
|
+
processCallback: m
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
export {
|
|
156
|
+
E as createCustomer,
|
|
157
|
+
R as decodeToken,
|
|
158
|
+
C as normalizeRedirectUri,
|
|
159
|
+
A as performGoogleLogin,
|
|
160
|
+
S as refreshToken,
|
|
161
|
+
x as retrieveCustomer,
|
|
162
|
+
V as useGoogleAuth,
|
|
163
|
+
M as validateRedirectUri
|
|
164
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(a,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],l):(a=typeof globalThis<"u"?globalThis:a||self,l(a.MedusaGoogleLoginLogic={},a.React))})(this,function(a,l){"use strict";function y(t,r){try{const e=new URL(t);return e.pathname=e.pathname.replace(/\/$/,""),r?e.protocol="http:":e.protocol="https:",e.toString().replace(/\/$/,"")}catch(e){return console.warn("Failed to normalize redirect URI:",e),t.replace(/\/$/,"")}}function I(t,r,e){const o=r?"http://localhost:8000/auth/customer/google/callback":`https://${e}/auth/customer/google/callback`,n=y(t,r),h=n===o;return h||console.warn("⚠️ Redirect URI validation mismatch:",{expected:o,actual:n,difference:n.replace(o,"")||o.replace(n,"")}),{valid:h,expected:o,actual:n}}async function U(t,r){try{return await t.auth.login("customer","google",{redirect_uri:r})}catch(e){throw console.error("❌ Google login initiation failed:",e),e}}function k(t){if(!t)return null;try{const r=t.split(".");if(r.length!==3)return null;const e=r[1],o=e+"=".repeat((4-e.length%4)%4),n=typeof Buffer<"u"?Buffer.from(o.replace(/-/g,"+").replace(/_/g,"/"),"base64").toString("utf-8"):atob(o.replace(/-/g,"+").replace(/_/g,"/"));return JSON.parse(n)}catch(r){return console.error("Failed to decode token:",r),null}}async function C(t){try{return await t.auth.refresh()}catch(r){return console.error("❌ Token refresh failed:",r),null}}async function v(t,r){try{const e={};return r&&(e.authorization=`Bearer ${r}`),(await t.client.fetch("/store/customers/me",{method:"GET",query:{fields:"*orders"},headers:e})).customer}catch(e){throw console.error("❌ Customer retrieval failed:",e),e}}async function G(t,r,e){try{return(await t.store.customer.create({email:r},{},{authorization:`Bearer ${e}`})).customer}catch(o){throw console.error("❌ Customer creation failed:",o),o}}const b={BASE_URL:"/",DEV:!1,MODE:"production",PROD:!0,SSR:!1},R=({sdk:t,baseUrl:r,productionDomain:e="bellyandbaby.co",callbackPath:o="/auth/customer/google/callback",queryParams:n,onSuccess:h,onError:d,onNeedsReLogin:g})=>{const[O,p]=l.useState(!1),[z,w]=l.useState(null),[A,_]=l.useState(null),[j,S]=l.useState(!1),$=c=>{var i;try{return((i=process.env)==null?void 0:i[c])||(b==null?void 0:b[c])}catch{return}},E=l.useCallback(async()=>{p(!0),w(null);try{const c=r||$("NEXT_PUBLIC_MEDUSA_STOREFRONT_URL")||$("NEXT_PUBLIC_BASE_URL")||(typeof window<"u"?window.location.origin:""),i=c.includes("localhost")||c.includes("127.0.0.1");let f=c;!i&&e&&c.includes(e)&&(f=`https://${e}`);const s=y(`${f}${o}`,i),u=await U(t,s);if(typeof u=="object"&&(u!=null&&u.location)){window.location.href=u.location;return}if(typeof u!="string")throw new Error("Unexpected authentication result");window.location.reload()}catch(c){console.error("Google login initiation failed:",c);const i=c.message||"Failed to initiate Google login";w(i),d==null||d(i),p(!1)}},[t,r,e,o,d]),T=l.useCallback(async()=>{var c,i;if(!(!n||!n.code)){p(!0),w(null);try{const f=await t.auth.callback("customer","google",n);if(typeof f!="string")throw new Error("Invalid token response");const s=k(f),u=(c=s==null?void 0:s.user_metadata)==null?void 0:c.email;if(!(s==null?void 0:s.actor_id)){if(!u)throw new Error("No email found in token");try{await G(t,u,f),S(!0),g==null||g(u),p(!1);return}catch(m){if(m.status===422||m.status===409||(i=m.message)!=null&&i.includes("already exists")){S(!0),g==null||g(u),p(!1);return}throw m}}const F=await C(t)||f,B=await v(t,F);_(B),h==null||h(B)}catch(f){console.error("Google callback processing failed:",f);const s=f.message||"Authentication failed";w(s),d==null||d(s)}finally{p(!1)}}},[t,n,h,d,g]);return l.useEffect(()=>{n&&n.code&&T()},[n,T]),{login:E,isLoading:O,error:z,customer:A,needsReLogin:j,processCallback:T}};a.createCustomer=G,a.decodeToken=k,a.normalizeRedirectUri=y,a.performGoogleLogin=U,a.refreshToken=C,a.retrieveCustomer=v,a.useGoogleAuth=R,a.validateRedirectUri=I,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "medusa-google-login-logic",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Ported Google Login logic from B&B_ui project.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/ui-library.umd.cjs",
|
|
7
|
+
"module": "./dist/ui-library.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/ui-library.js",
|
|
12
|
+
"require": "./dist/ui-library.umd.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "tsc && vite build",
|
|
22
|
+
"preview": "vite preview"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
26
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@medusajs/js-sdk": "^2.11.2"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.0.0",
|
|
33
|
+
"@types/react": "^18.0.0 || ^19.0.0",
|
|
34
|
+
"@types/react-dom": "^18.0.0 || ^19.0.0",
|
|
35
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
36
|
+
"react": "^19.0.0",
|
|
37
|
+
"react-dom": "^19.0.0",
|
|
38
|
+
"typescript": "^5.0.0",
|
|
39
|
+
"vite": "^5.0.0",
|
|
40
|
+
"vite-plugin-dts": "^3.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|