@blinkdotnew/sdk 0.19.0 → 0.19.4
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 +44 -7
- package/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +216 -13
- package/dist/index.mjs +216 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -146,7 +146,7 @@ This SDK powers every Blink-generated app with:
|
|
|
146
146
|
|
|
147
147
|
- **🔐 Authentication**: Flexible auth system with managed (redirect) and headless (custom UI) modes, email/password, social providers (Google, GitHub, Apple, Microsoft), magic links, RBAC, and custom email branding
|
|
148
148
|
- **🗄️ Database**: PostgREST-compatible CRUD operations with advanced filtering
|
|
149
|
-
- **🤖 AI**:
|
|
149
|
+
- **🤖 AI**: Multi-model image generation & editing (10 models), text generation with web search, object generation, speech synthesis, and transcription
|
|
150
150
|
- **📄 Data**: Extract text content from documents, secure API proxy with secret substitution, web scraping, screenshots, and web search
|
|
151
151
|
- **📁 Storage**: File upload, download, and management
|
|
152
152
|
- **📧 Notifications**: Email sending with attachments, custom branding, and delivery tracking
|
|
@@ -708,29 +708,56 @@ const { object: todoList } = await blink.ai.generateObject({
|
|
|
708
708
|
// })
|
|
709
709
|
// Error: "schema must be a JSON Schema of 'type: \"object\"', got 'type: \"array\"'"
|
|
710
710
|
|
|
711
|
-
// Generate and modify images with AI
|
|
712
|
-
// 🔥
|
|
711
|
+
// Generate and modify images with AI - Multi-Model Support (10 models available)
|
|
712
|
+
// 🔥 Choose between fast generation or high-quality results
|
|
713
713
|
// 🎨 For style transfer: provide ALL images in the images array, don't reference URLs in prompts
|
|
714
714
|
|
|
715
|
-
// Basic image generation
|
|
715
|
+
// Basic image generation (uses default fast model: fal-ai/nano-banana)
|
|
716
716
|
const { data } = await blink.ai.generateImage({
|
|
717
717
|
prompt: 'A serene landscape with mountains and a lake at sunset'
|
|
718
718
|
})
|
|
719
719
|
console.log('Image URL:', data[0].url)
|
|
720
720
|
|
|
721
|
-
//
|
|
721
|
+
// High-quality generation with Pro model
|
|
722
|
+
const { data: proImage } = await blink.ai.generateImage({
|
|
723
|
+
prompt: 'A detailed infographic about AI with charts and diagrams',
|
|
724
|
+
model: 'fal-ai/nano-banana-pro', // High quality model
|
|
725
|
+
n: 1,
|
|
726
|
+
size: '1792x1024' // Custom size
|
|
727
|
+
})
|
|
728
|
+
|
|
729
|
+
// Generate multiple variations
|
|
722
730
|
const { data } = await blink.ai.generateImage({
|
|
723
731
|
prompt: 'A futuristic robot in different poses',
|
|
732
|
+
model: 'fal-ai/nano-banana', // Fast model
|
|
724
733
|
n: 3
|
|
725
734
|
})
|
|
726
735
|
data.forEach((img, i) => console.log(`Image ${i+1}:`, img.url))
|
|
727
736
|
|
|
728
|
-
|
|
737
|
+
**Available Models for Text-to-Image:**
|
|
738
|
+
|
|
739
|
+
| Model | Speed | Quality | Best For |
|
|
740
|
+
|-------|-------|---------|----------|
|
|
741
|
+
| `fal-ai/nano-banana` (default) | ⚡ Fast | Good | Prototypes, high-volume generation |
|
|
742
|
+
| `fal-ai/nano-banana-pro` | Standard | ⭐ Excellent | Marketing materials, high-fidelity visuals |
|
|
743
|
+
| `fal-ai/gemini-25-flash-image` | ⚡ Fast | Good | Alias for `nano-banana` |
|
|
744
|
+
| `fal-ai/gemini-3-pro-image-preview` | Standard | ⭐ Excellent | Alias for `nano-banana-pro` |
|
|
745
|
+
| `gemini-2.5-flash-image-preview` | ⚡ Fast | Good | Legacy - Direct Gemini API |
|
|
746
|
+
| `gemini-3-pro-image-preview` | Standard | ⭐ Excellent | Legacy - Direct Gemini API |
|
|
747
|
+
|
|
748
|
+
// Image editing - transform existing images with prompts (uses default fast model)
|
|
729
749
|
const { data: headshots } = await blink.ai.modifyImage({
|
|
730
|
-
images: ['https://storage.example.com/user-photo.jpg'], // Up to
|
|
750
|
+
images: ['https://storage.example.com/user-photo.jpg'], // Up to 16 images supported!
|
|
731
751
|
prompt: 'Transform into professional business headshot with studio lighting'
|
|
732
752
|
})
|
|
733
753
|
|
|
754
|
+
// High-quality editing with Pro model
|
|
755
|
+
const { data: proEdited } = await blink.ai.modifyImage({
|
|
756
|
+
images: ['https://storage.example.com/portrait.jpg'],
|
|
757
|
+
prompt: 'Add a majestic ancient tree in the background with glowing leaves',
|
|
758
|
+
model: 'fal-ai/nano-banana-pro/edit' // High quality editing
|
|
759
|
+
})
|
|
760
|
+
|
|
734
761
|
// Advanced image editing with multiple input images
|
|
735
762
|
const { data } = await blink.ai.modifyImage({
|
|
736
763
|
images: [
|
|
@@ -739,9 +766,19 @@ const { data } = await blink.ai.modifyImage({
|
|
|
739
766
|
'https://storage.example.com/photo3.jpg'
|
|
740
767
|
],
|
|
741
768
|
prompt: 'Combine these architectural styles into a futuristic building design',
|
|
769
|
+
model: 'fal-ai/nano-banana/edit', // Fast editing
|
|
742
770
|
n: 2
|
|
743
771
|
})
|
|
744
772
|
|
|
773
|
+
**Available Models for Image Editing:**
|
|
774
|
+
|
|
775
|
+
| Model | Speed | Quality | Best For |
|
|
776
|
+
|-------|-------|---------|----------|
|
|
777
|
+
| `fal-ai/nano-banana/edit` (default) | ⚡ Fast | Good | Quick adjustments, style transfers |
|
|
778
|
+
| `fal-ai/nano-banana-pro/edit` | Standard | ⭐ Excellent | Detailed retouching, complex edits |
|
|
779
|
+
| `fal-ai/gemini-25-flash-image/edit` | ⚡ Fast | Good | Alias for `nano-banana/edit` |
|
|
780
|
+
| `fal-ai/gemini-3-pro-image-preview/edit` | Standard | ⭐ Excellent | Alias for `nano-banana-pro/edit` |
|
|
781
|
+
|
|
745
782
|
// 🎨 Style Transfer & Feature Application
|
|
746
783
|
// ⚠️ IMPORTANT: When applying styles/features from one image to another,
|
|
747
784
|
// provide ALL images in the array - don't reference images in the prompt text
|
package/dist/index.d.mts
CHANGED
|
@@ -118,6 +118,11 @@ type AuthProvider = 'email' | 'google' | 'github' | 'apple' | 'microsoft' | 'twi
|
|
|
118
118
|
interface AuthOptions {
|
|
119
119
|
redirectUrl?: string;
|
|
120
120
|
metadata?: Record<string, any>;
|
|
121
|
+
/**
|
|
122
|
+
* Force redirect flow instead of popup (useful for Safari or when popups are blocked)
|
|
123
|
+
* When true, always uses redirect flow regardless of browser detection
|
|
124
|
+
*/
|
|
125
|
+
forceRedirect?: boolean;
|
|
121
126
|
}
|
|
122
127
|
interface SignUpData {
|
|
123
128
|
email: string;
|
|
@@ -1008,8 +1013,14 @@ declare class BlinkAuth {
|
|
|
1008
1013
|
* Sign in with Microsoft (headless mode)
|
|
1009
1014
|
*/
|
|
1010
1015
|
signInWithMicrosoft(options?: AuthOptions): Promise<BlinkUser>;
|
|
1016
|
+
/**
|
|
1017
|
+
* Check if current browser is Safari (desktop, iPhone, iPad)
|
|
1018
|
+
* Safari has strict popup blocking, so we must use redirect flow
|
|
1019
|
+
*/
|
|
1020
|
+
private isSafari;
|
|
1011
1021
|
/**
|
|
1012
1022
|
* Generic provider sign-in method (headless mode)
|
|
1023
|
+
* Uses redirect flow for Safari, popup flow for other browsers
|
|
1013
1024
|
*/
|
|
1014
1025
|
signInWithProvider(provider: AuthProvider, options?: AuthOptions): Promise<BlinkUser>;
|
|
1015
1026
|
/**
|
|
@@ -1114,6 +1125,11 @@ declare class BlinkAuth {
|
|
|
1114
1125
|
private setTokens;
|
|
1115
1126
|
private clearTokens;
|
|
1116
1127
|
private getStoredTokens;
|
|
1128
|
+
/**
|
|
1129
|
+
* Extract URL parameters from both search params and hash fragments
|
|
1130
|
+
* Safari OAuth redirects often use hash fragments instead of query params
|
|
1131
|
+
*/
|
|
1132
|
+
private extractUrlParams;
|
|
1117
1133
|
private extractTokensFromUrl;
|
|
1118
1134
|
private clearUrlTokens;
|
|
1119
1135
|
private redirectToAuth;
|
package/dist/index.d.ts
CHANGED
|
@@ -118,6 +118,11 @@ type AuthProvider = 'email' | 'google' | 'github' | 'apple' | 'microsoft' | 'twi
|
|
|
118
118
|
interface AuthOptions {
|
|
119
119
|
redirectUrl?: string;
|
|
120
120
|
metadata?: Record<string, any>;
|
|
121
|
+
/**
|
|
122
|
+
* Force redirect flow instead of popup (useful for Safari or when popups are blocked)
|
|
123
|
+
* When true, always uses redirect flow regardless of browser detection
|
|
124
|
+
*/
|
|
125
|
+
forceRedirect?: boolean;
|
|
121
126
|
}
|
|
122
127
|
interface SignUpData {
|
|
123
128
|
email: string;
|
|
@@ -1008,8 +1013,14 @@ declare class BlinkAuth {
|
|
|
1008
1013
|
* Sign in with Microsoft (headless mode)
|
|
1009
1014
|
*/
|
|
1010
1015
|
signInWithMicrosoft(options?: AuthOptions): Promise<BlinkUser>;
|
|
1016
|
+
/**
|
|
1017
|
+
* Check if current browser is Safari (desktop, iPhone, iPad)
|
|
1018
|
+
* Safari has strict popup blocking, so we must use redirect flow
|
|
1019
|
+
*/
|
|
1020
|
+
private isSafari;
|
|
1011
1021
|
/**
|
|
1012
1022
|
* Generic provider sign-in method (headless mode)
|
|
1023
|
+
* Uses redirect flow for Safari, popup flow for other browsers
|
|
1013
1024
|
*/
|
|
1014
1025
|
signInWithProvider(provider: AuthProvider, options?: AuthOptions): Promise<BlinkUser>;
|
|
1015
1026
|
/**
|
|
@@ -1114,6 +1125,11 @@ declare class BlinkAuth {
|
|
|
1114
1125
|
private setTokens;
|
|
1115
1126
|
private clearTokens;
|
|
1116
1127
|
private getStoredTokens;
|
|
1128
|
+
/**
|
|
1129
|
+
* Extract URL parameters from both search params and hash fragments
|
|
1130
|
+
* Safari OAuth redirects often use hash fragments instead of query params
|
|
1131
|
+
*/
|
|
1132
|
+
private extractUrlParams;
|
|
1117
1133
|
private extractTokensFromUrl;
|
|
1118
1134
|
private clearUrlTokens;
|
|
1119
1135
|
private redirectToAuth;
|
package/dist/index.js
CHANGED
|
@@ -1144,6 +1144,36 @@ var BlinkAuth = class {
|
|
|
1144
1144
|
return;
|
|
1145
1145
|
}
|
|
1146
1146
|
}
|
|
1147
|
+
try {
|
|
1148
|
+
if (typeof window !== "undefined") {
|
|
1149
|
+
console.log("\u{1F50D} Checking URL for errors/tokens:", {
|
|
1150
|
+
href: window.location.href,
|
|
1151
|
+
search: window.location.search,
|
|
1152
|
+
hash: window.location.hash
|
|
1153
|
+
});
|
|
1154
|
+
const urlParams = this.extractUrlParams();
|
|
1155
|
+
const errorParam = urlParams.get("error");
|
|
1156
|
+
if (errorParam) {
|
|
1157
|
+
const errorCode = this.mapErrorCodeFromResponse(errorParam);
|
|
1158
|
+
const errorMessage = urlParams.get("error_description") || "Authentication failed";
|
|
1159
|
+
const error = new BlinkAuthError(errorCode, errorMessage);
|
|
1160
|
+
console.error("\u274C Auth error in URL:", {
|
|
1161
|
+
error: errorParam,
|
|
1162
|
+
errorMessage,
|
|
1163
|
+
allParams: Object.fromEntries(urlParams.entries())
|
|
1164
|
+
});
|
|
1165
|
+
if (typeof window !== "undefined") {
|
|
1166
|
+
window.dispatchEvent(new CustomEvent("blink:auth:error", {
|
|
1167
|
+
detail: { error, errorMessage }
|
|
1168
|
+
}));
|
|
1169
|
+
}
|
|
1170
|
+
this.clearUrlTokens();
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
} catch (error) {
|
|
1175
|
+
console.error("Error handling failed redirect:", error);
|
|
1176
|
+
}
|
|
1147
1177
|
const tokensFromUrl = this.extractTokensFromUrl();
|
|
1148
1178
|
if (tokensFromUrl) {
|
|
1149
1179
|
console.log("\u{1F4E5} Found tokens in URL, setting them...");
|
|
@@ -1151,6 +1181,31 @@ var BlinkAuth = class {
|
|
|
1151
1181
|
this.clearUrlTokens();
|
|
1152
1182
|
console.log("\u2705 Auth initialization complete (from URL)");
|
|
1153
1183
|
return;
|
|
1184
|
+
} else {
|
|
1185
|
+
console.log("\u26A0\uFE0F No tokens found in URL after redirect - checking if this was a redirect callback");
|
|
1186
|
+
if (typeof window !== "undefined") {
|
|
1187
|
+
const urlParams = this.extractUrlParams();
|
|
1188
|
+
const state = urlParams.get("state");
|
|
1189
|
+
if (state) {
|
|
1190
|
+
console.log("\u26A0\uFE0F State found in URL but no tokens - redirect may have failed silently");
|
|
1191
|
+
try {
|
|
1192
|
+
const expectedState = sessionStorage.getItem("blink_oauth_state");
|
|
1193
|
+
if (expectedState === state) {
|
|
1194
|
+
console.error("\u274C Redirect callback received but no tokens - authentication may have failed");
|
|
1195
|
+
const errorMessage = "Authentication failed. Please try again.";
|
|
1196
|
+
if (typeof window !== "undefined") {
|
|
1197
|
+
window.dispatchEvent(new CustomEvent("blink:auth:error", {
|
|
1198
|
+
detail: { error: new BlinkAuthError("NETWORK_ERROR" /* NETWORK_ERROR */, errorMessage), errorMessage }
|
|
1199
|
+
}));
|
|
1200
|
+
}
|
|
1201
|
+
this.clearUrlTokens();
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
} catch (e) {
|
|
1205
|
+
console.error("Error checking sessionStorage:", e);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1154
1209
|
}
|
|
1155
1210
|
const storedTokens = await this.getStoredTokens();
|
|
1156
1211
|
if (storedTokens) {
|
|
@@ -1504,37 +1559,145 @@ var BlinkAuth = class {
|
|
|
1504
1559
|
}
|
|
1505
1560
|
return this.signInWithProvider("microsoft", options);
|
|
1506
1561
|
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Check if current browser is Safari (desktop, iPhone, iPad)
|
|
1564
|
+
* Safari has strict popup blocking, so we must use redirect flow
|
|
1565
|
+
*/
|
|
1566
|
+
isSafari() {
|
|
1567
|
+
if (typeof window === "undefined") return false;
|
|
1568
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
1569
|
+
const hasSafariUA = /safari/i.test(ua) && !/chrome|chromium|android|edg|firefox|opera/i.test(ua);
|
|
1570
|
+
const isSafariVendor = typeof navigator !== "undefined" && !!navigator.vendor && /apple/i.test(navigator.vendor);
|
|
1571
|
+
const isIOSSafari = /safari/i.test(ua) && /iphone|ipad|ipod/i.test(ua);
|
|
1572
|
+
const hasSafariProperties = typeof window !== "undefined" && ("safari" in window || window.safari !== void 0) && !("chrome" in window);
|
|
1573
|
+
const isMacSafari = /macintosh/i.test(ua) && isSafariVendor && !/chrome|chromium|firefox|edg/i.test(ua);
|
|
1574
|
+
const isSafari = !!(hasSafariUA || isSafariVendor || isIOSSafari || hasSafariProperties || isMacSafari);
|
|
1575
|
+
if (isSafari) {
|
|
1576
|
+
console.log("\u{1F34E} Safari browser detected - will use redirect flow", {
|
|
1577
|
+
hasSafariUA,
|
|
1578
|
+
isSafariVendor,
|
|
1579
|
+
isIOSSafari,
|
|
1580
|
+
hasSafariProperties,
|
|
1581
|
+
isMacSafari,
|
|
1582
|
+
userAgent: ua.substring(0, 100),
|
|
1583
|
+
vendor: navigator.vendor
|
|
1584
|
+
});
|
|
1585
|
+
} else {
|
|
1586
|
+
console.log("\u{1F50D} Not detected as Safari:", {
|
|
1587
|
+
hasSafariUA,
|
|
1588
|
+
isSafariVendor,
|
|
1589
|
+
isIOSSafari,
|
|
1590
|
+
hasSafariProperties,
|
|
1591
|
+
isMacSafari,
|
|
1592
|
+
userAgent: ua.substring(0, 100),
|
|
1593
|
+
vendor: navigator.vendor
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
return isSafari;
|
|
1597
|
+
}
|
|
1507
1598
|
/**
|
|
1508
1599
|
* Generic provider sign-in method (headless mode)
|
|
1600
|
+
* Uses redirect flow for Safari, popup flow for other browsers
|
|
1509
1601
|
*/
|
|
1510
1602
|
async signInWithProvider(provider, options) {
|
|
1511
1603
|
if (this.authConfig.mode !== "headless") {
|
|
1512
1604
|
throw new BlinkAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "signInWithProvider is only available in headless mode");
|
|
1513
1605
|
}
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1606
|
+
const state = this.generateState();
|
|
1607
|
+
let redirectUrl = options?.redirectUrl || "";
|
|
1608
|
+
if (!redirectUrl && typeof window !== "undefined") {
|
|
1609
|
+
if (window.location.href.startsWith("http")) {
|
|
1610
|
+
redirectUrl = window.location.href;
|
|
1611
|
+
} else {
|
|
1612
|
+
redirectUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
1521
1613
|
}
|
|
1522
|
-
|
|
1614
|
+
}
|
|
1615
|
+
try {
|
|
1616
|
+
const redirectUrlObj = new URL(redirectUrl);
|
|
1617
|
+
redirectUrlObj.searchParams.delete("access_token");
|
|
1618
|
+
redirectUrlObj.searchParams.delete("refresh_token");
|
|
1619
|
+
redirectUrlObj.searchParams.delete("state");
|
|
1620
|
+
redirectUrlObj.searchParams.delete("error");
|
|
1621
|
+
redirectUrlObj.searchParams.delete("error_description");
|
|
1622
|
+
redirectUrlObj.hash = "";
|
|
1623
|
+
redirectUrl = redirectUrlObj.toString();
|
|
1624
|
+
} catch (e) {
|
|
1625
|
+
console.warn("Failed to clean redirect URL:", e);
|
|
1626
|
+
}
|
|
1627
|
+
try {
|
|
1628
|
+
if (typeof window !== "undefined") {
|
|
1629
|
+
sessionStorage.setItem("blink_oauth_state", state);
|
|
1630
|
+
sessionStorage.setItem("blink_oauth_redirect_url", redirectUrl);
|
|
1631
|
+
console.log("\u{1F4BE} Stored OAuth state:", { state, redirectUrl });
|
|
1632
|
+
}
|
|
1633
|
+
} catch (e) {
|
|
1634
|
+
console.warn("Failed to store OAuth state in sessionStorage:", e);
|
|
1635
|
+
}
|
|
1636
|
+
const isSafari = this.isSafari();
|
|
1637
|
+
const forceRedirect = options?.forceRedirect === true;
|
|
1638
|
+
if ((isSafari || forceRedirect) && typeof window !== "undefined") {
|
|
1639
|
+
console.log("\u{1F310} Using redirect flow (no popup)", {
|
|
1640
|
+
provider,
|
|
1641
|
+
projectId: this.config.projectId,
|
|
1642
|
+
state,
|
|
1643
|
+
redirectUrl,
|
|
1644
|
+
authUrl: this.authUrl,
|
|
1645
|
+
reason: isSafari ? "Safari detected" : "forceRedirect option set"
|
|
1646
|
+
});
|
|
1647
|
+
const authRedirectUrl = new URL("/auth", this.authUrl);
|
|
1648
|
+
authRedirectUrl.searchParams.set("provider", provider);
|
|
1649
|
+
authRedirectUrl.searchParams.set("project_id", this.config.projectId);
|
|
1650
|
+
authRedirectUrl.searchParams.set("state", state);
|
|
1651
|
+
authRedirectUrl.searchParams.set("mode", "redirect");
|
|
1652
|
+
authRedirectUrl.searchParams.set("redirect_url", redirectUrl);
|
|
1653
|
+
console.log("\u{1F504} Redirecting to:", authRedirectUrl.toString());
|
|
1654
|
+
window.location.href = authRedirectUrl.toString();
|
|
1655
|
+
return new Promise(() => {
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
return new Promise((resolve, reject) => {
|
|
1523
1659
|
const popupUrl = new URL("/auth", this.authUrl);
|
|
1524
1660
|
popupUrl.searchParams.set("provider", provider);
|
|
1525
1661
|
popupUrl.searchParams.set("project_id", this.config.projectId);
|
|
1526
1662
|
popupUrl.searchParams.set("state", state);
|
|
1527
1663
|
popupUrl.searchParams.set("mode", "popup");
|
|
1528
1664
|
popupUrl.searchParams.set("redirect_url", redirectUrl);
|
|
1665
|
+
if (typeof window !== "undefined") {
|
|
1666
|
+
popupUrl.searchParams.set("opener_origin", window.location.origin);
|
|
1667
|
+
}
|
|
1529
1668
|
const popup = window.open(
|
|
1530
1669
|
popupUrl.toString(),
|
|
1531
1670
|
"blink-auth",
|
|
1532
1671
|
"width=500,height=600,scrollbars=yes,resizable=yes"
|
|
1533
1672
|
);
|
|
1534
1673
|
if (!popup) {
|
|
1535
|
-
|
|
1674
|
+
console.warn("\u26A0\uFE0F Popup was blocked, falling back to redirect flow");
|
|
1675
|
+
const authRedirectUrl = new URL("/auth", this.authUrl);
|
|
1676
|
+
authRedirectUrl.searchParams.set("provider", provider);
|
|
1677
|
+
authRedirectUrl.searchParams.set("project_id", this.config.projectId);
|
|
1678
|
+
authRedirectUrl.searchParams.set("state", state);
|
|
1679
|
+
authRedirectUrl.searchParams.set("mode", "redirect");
|
|
1680
|
+
authRedirectUrl.searchParams.set("redirect_url", redirectUrl);
|
|
1681
|
+
console.log("\u{1F504} Falling back to redirect:", authRedirectUrl.toString());
|
|
1682
|
+
window.location.href = authRedirectUrl.toString();
|
|
1536
1683
|
return;
|
|
1537
1684
|
}
|
|
1685
|
+
try {
|
|
1686
|
+
if (popup.closed || !popup.window) {
|
|
1687
|
+
console.warn("\u26A0\uFE0F Popup appears to be blocked (closed immediately), falling back to redirect flow");
|
|
1688
|
+
const authRedirectUrl = new URL("/auth", this.authUrl);
|
|
1689
|
+
authRedirectUrl.searchParams.set("provider", provider);
|
|
1690
|
+
authRedirectUrl.searchParams.set("project_id", this.config.projectId);
|
|
1691
|
+
authRedirectUrl.searchParams.set("state", state);
|
|
1692
|
+
authRedirectUrl.searchParams.set("mode", "redirect");
|
|
1693
|
+
authRedirectUrl.searchParams.set("redirect_url", redirectUrl);
|
|
1694
|
+
console.log("\u{1F504} Falling back to redirect:", authRedirectUrl.toString());
|
|
1695
|
+
window.location.href = authRedirectUrl.toString();
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
} catch (e) {
|
|
1699
|
+
console.log("\u26A0\uFE0F Could not verify popup state, assuming it opened successfully");
|
|
1700
|
+
}
|
|
1538
1701
|
let timeoutId;
|
|
1539
1702
|
const messageListener = (event) => {
|
|
1540
1703
|
let allowed = false;
|
|
@@ -1544,7 +1707,12 @@ var BlinkAuth = class {
|
|
|
1544
1707
|
} catch {
|
|
1545
1708
|
}
|
|
1546
1709
|
if (event.origin === "http://localhost:3000" || event.origin === "http://localhost:3001") allowed = true;
|
|
1547
|
-
if (
|
|
1710
|
+
if (typeof window !== "undefined" && event.origin === window.location.origin) allowed = true;
|
|
1711
|
+
if (!allowed) {
|
|
1712
|
+
console.log("\u{1F6AB} Blocked postMessage from untrusted origin:", event.origin);
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
console.log("\u2705 Accepted postMessage from origin:", event.origin);
|
|
1548
1716
|
if (event.data?.type === "BLINK_AUTH_TOKENS") {
|
|
1549
1717
|
const { access_token, refresh_token, token_type, expires_in, refresh_expires_in, projectId, state: returnedState } = event.data;
|
|
1550
1718
|
try {
|
|
@@ -2304,17 +2472,50 @@ var BlinkAuth = class {
|
|
|
2304
2472
|
return null;
|
|
2305
2473
|
}
|
|
2306
2474
|
}
|
|
2475
|
+
/**
|
|
2476
|
+
* Extract URL parameters from both search params and hash fragments
|
|
2477
|
+
* Safari OAuth redirects often use hash fragments instead of query params
|
|
2478
|
+
*/
|
|
2479
|
+
extractUrlParams() {
|
|
2480
|
+
const params = new URLSearchParams();
|
|
2481
|
+
if (typeof window !== "undefined" && window.location.search) {
|
|
2482
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
2483
|
+
for (const [key, value] of searchParams.entries()) {
|
|
2484
|
+
params.set(key, value);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
if (typeof window !== "undefined" && window.location.hash) {
|
|
2488
|
+
const hash = window.location.hash.substring(1);
|
|
2489
|
+
const hashParams = new URLSearchParams(hash);
|
|
2490
|
+
for (const [key, value] of hashParams.entries()) {
|
|
2491
|
+
if (!params.has(key)) {
|
|
2492
|
+
params.set(key, value);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
return params;
|
|
2497
|
+
}
|
|
2307
2498
|
extractTokensFromUrl() {
|
|
2308
2499
|
if (typeof window === "undefined") return null;
|
|
2309
|
-
const params =
|
|
2500
|
+
const params = this.extractUrlParams();
|
|
2310
2501
|
const accessToken = params.get("access_token");
|
|
2311
2502
|
const refreshToken = params.get("refresh_token");
|
|
2503
|
+
const state = params.get("state");
|
|
2504
|
+
const error = params.get("error");
|
|
2312
2505
|
console.log("\u{1F50D} Extracting tokens from URL:", {
|
|
2313
2506
|
url: window.location.href,
|
|
2507
|
+
search: window.location.search,
|
|
2508
|
+
hash: window.location.hash,
|
|
2314
2509
|
accessToken: accessToken ? `${accessToken.substring(0, 20)}...` : null,
|
|
2315
2510
|
refreshToken: refreshToken ? `${refreshToken.substring(0, 20)}...` : null,
|
|
2511
|
+
state: state ? `${state.substring(0, 10)}...` : null,
|
|
2512
|
+
error: error || null,
|
|
2316
2513
|
allParams: Object.fromEntries(params.entries())
|
|
2317
2514
|
});
|
|
2515
|
+
if (error) {
|
|
2516
|
+
console.log("\u274C Error parameter found in URL, not extracting tokens");
|
|
2517
|
+
return null;
|
|
2518
|
+
}
|
|
2318
2519
|
if (accessToken) {
|
|
2319
2520
|
const tokens = {
|
|
2320
2521
|
access_token: accessToken,
|
|
@@ -2329,7 +2530,8 @@ var BlinkAuth = class {
|
|
|
2329
2530
|
};
|
|
2330
2531
|
console.log("\u2705 Tokens extracted successfully:", {
|
|
2331
2532
|
hasAccessToken: !!tokens.access_token,
|
|
2332
|
-
hasRefreshToken: !!tokens.refresh_token
|
|
2533
|
+
hasRefreshToken: !!tokens.refresh_token,
|
|
2534
|
+
state: state || "no state"
|
|
2333
2535
|
});
|
|
2334
2536
|
return tokens;
|
|
2335
2537
|
}
|
|
@@ -2349,8 +2551,9 @@ var BlinkAuth = class {
|
|
|
2349
2551
|
url.searchParams.delete("code");
|
|
2350
2552
|
url.searchParams.delete("error");
|
|
2351
2553
|
url.searchParams.delete("error_description");
|
|
2554
|
+
url.hash = "";
|
|
2352
2555
|
window.history.replaceState({}, "", url.toString());
|
|
2353
|
-
console.log("\u{1F9F9} URL cleaned up, removed auth parameters");
|
|
2556
|
+
console.log("\u{1F9F9} URL cleaned up, removed auth parameters from both search and hash");
|
|
2354
2557
|
}
|
|
2355
2558
|
redirectToAuth() {
|
|
2356
2559
|
if (typeof window !== "undefined") {
|
package/dist/index.mjs
CHANGED
|
@@ -1142,6 +1142,36 @@ var BlinkAuth = class {
|
|
|
1142
1142
|
return;
|
|
1143
1143
|
}
|
|
1144
1144
|
}
|
|
1145
|
+
try {
|
|
1146
|
+
if (typeof window !== "undefined") {
|
|
1147
|
+
console.log("\u{1F50D} Checking URL for errors/tokens:", {
|
|
1148
|
+
href: window.location.href,
|
|
1149
|
+
search: window.location.search,
|
|
1150
|
+
hash: window.location.hash
|
|
1151
|
+
});
|
|
1152
|
+
const urlParams = this.extractUrlParams();
|
|
1153
|
+
const errorParam = urlParams.get("error");
|
|
1154
|
+
if (errorParam) {
|
|
1155
|
+
const errorCode = this.mapErrorCodeFromResponse(errorParam);
|
|
1156
|
+
const errorMessage = urlParams.get("error_description") || "Authentication failed";
|
|
1157
|
+
const error = new BlinkAuthError(errorCode, errorMessage);
|
|
1158
|
+
console.error("\u274C Auth error in URL:", {
|
|
1159
|
+
error: errorParam,
|
|
1160
|
+
errorMessage,
|
|
1161
|
+
allParams: Object.fromEntries(urlParams.entries())
|
|
1162
|
+
});
|
|
1163
|
+
if (typeof window !== "undefined") {
|
|
1164
|
+
window.dispatchEvent(new CustomEvent("blink:auth:error", {
|
|
1165
|
+
detail: { error, errorMessage }
|
|
1166
|
+
}));
|
|
1167
|
+
}
|
|
1168
|
+
this.clearUrlTokens();
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
} catch (error) {
|
|
1173
|
+
console.error("Error handling failed redirect:", error);
|
|
1174
|
+
}
|
|
1145
1175
|
const tokensFromUrl = this.extractTokensFromUrl();
|
|
1146
1176
|
if (tokensFromUrl) {
|
|
1147
1177
|
console.log("\u{1F4E5} Found tokens in URL, setting them...");
|
|
@@ -1149,6 +1179,31 @@ var BlinkAuth = class {
|
|
|
1149
1179
|
this.clearUrlTokens();
|
|
1150
1180
|
console.log("\u2705 Auth initialization complete (from URL)");
|
|
1151
1181
|
return;
|
|
1182
|
+
} else {
|
|
1183
|
+
console.log("\u26A0\uFE0F No tokens found in URL after redirect - checking if this was a redirect callback");
|
|
1184
|
+
if (typeof window !== "undefined") {
|
|
1185
|
+
const urlParams = this.extractUrlParams();
|
|
1186
|
+
const state = urlParams.get("state");
|
|
1187
|
+
if (state) {
|
|
1188
|
+
console.log("\u26A0\uFE0F State found in URL but no tokens - redirect may have failed silently");
|
|
1189
|
+
try {
|
|
1190
|
+
const expectedState = sessionStorage.getItem("blink_oauth_state");
|
|
1191
|
+
if (expectedState === state) {
|
|
1192
|
+
console.error("\u274C Redirect callback received but no tokens - authentication may have failed");
|
|
1193
|
+
const errorMessage = "Authentication failed. Please try again.";
|
|
1194
|
+
if (typeof window !== "undefined") {
|
|
1195
|
+
window.dispatchEvent(new CustomEvent("blink:auth:error", {
|
|
1196
|
+
detail: { error: new BlinkAuthError("NETWORK_ERROR" /* NETWORK_ERROR */, errorMessage), errorMessage }
|
|
1197
|
+
}));
|
|
1198
|
+
}
|
|
1199
|
+
this.clearUrlTokens();
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
} catch (e) {
|
|
1203
|
+
console.error("Error checking sessionStorage:", e);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1152
1207
|
}
|
|
1153
1208
|
const storedTokens = await this.getStoredTokens();
|
|
1154
1209
|
if (storedTokens) {
|
|
@@ -1502,37 +1557,145 @@ var BlinkAuth = class {
|
|
|
1502
1557
|
}
|
|
1503
1558
|
return this.signInWithProvider("microsoft", options);
|
|
1504
1559
|
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Check if current browser is Safari (desktop, iPhone, iPad)
|
|
1562
|
+
* Safari has strict popup blocking, so we must use redirect flow
|
|
1563
|
+
*/
|
|
1564
|
+
isSafari() {
|
|
1565
|
+
if (typeof window === "undefined") return false;
|
|
1566
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
1567
|
+
const hasSafariUA = /safari/i.test(ua) && !/chrome|chromium|android|edg|firefox|opera/i.test(ua);
|
|
1568
|
+
const isSafariVendor = typeof navigator !== "undefined" && !!navigator.vendor && /apple/i.test(navigator.vendor);
|
|
1569
|
+
const isIOSSafari = /safari/i.test(ua) && /iphone|ipad|ipod/i.test(ua);
|
|
1570
|
+
const hasSafariProperties = typeof window !== "undefined" && ("safari" in window || window.safari !== void 0) && !("chrome" in window);
|
|
1571
|
+
const isMacSafari = /macintosh/i.test(ua) && isSafariVendor && !/chrome|chromium|firefox|edg/i.test(ua);
|
|
1572
|
+
const isSafari = !!(hasSafariUA || isSafariVendor || isIOSSafari || hasSafariProperties || isMacSafari);
|
|
1573
|
+
if (isSafari) {
|
|
1574
|
+
console.log("\u{1F34E} Safari browser detected - will use redirect flow", {
|
|
1575
|
+
hasSafariUA,
|
|
1576
|
+
isSafariVendor,
|
|
1577
|
+
isIOSSafari,
|
|
1578
|
+
hasSafariProperties,
|
|
1579
|
+
isMacSafari,
|
|
1580
|
+
userAgent: ua.substring(0, 100),
|
|
1581
|
+
vendor: navigator.vendor
|
|
1582
|
+
});
|
|
1583
|
+
} else {
|
|
1584
|
+
console.log("\u{1F50D} Not detected as Safari:", {
|
|
1585
|
+
hasSafariUA,
|
|
1586
|
+
isSafariVendor,
|
|
1587
|
+
isIOSSafari,
|
|
1588
|
+
hasSafariProperties,
|
|
1589
|
+
isMacSafari,
|
|
1590
|
+
userAgent: ua.substring(0, 100),
|
|
1591
|
+
vendor: navigator.vendor
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
return isSafari;
|
|
1595
|
+
}
|
|
1505
1596
|
/**
|
|
1506
1597
|
* Generic provider sign-in method (headless mode)
|
|
1598
|
+
* Uses redirect flow for Safari, popup flow for other browsers
|
|
1507
1599
|
*/
|
|
1508
1600
|
async signInWithProvider(provider, options) {
|
|
1509
1601
|
if (this.authConfig.mode !== "headless") {
|
|
1510
1602
|
throw new BlinkAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "signInWithProvider is only available in headless mode");
|
|
1511
1603
|
}
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1604
|
+
const state = this.generateState();
|
|
1605
|
+
let redirectUrl = options?.redirectUrl || "";
|
|
1606
|
+
if (!redirectUrl && typeof window !== "undefined") {
|
|
1607
|
+
if (window.location.href.startsWith("http")) {
|
|
1608
|
+
redirectUrl = window.location.href;
|
|
1609
|
+
} else {
|
|
1610
|
+
redirectUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
1519
1611
|
}
|
|
1520
|
-
|
|
1612
|
+
}
|
|
1613
|
+
try {
|
|
1614
|
+
const redirectUrlObj = new URL(redirectUrl);
|
|
1615
|
+
redirectUrlObj.searchParams.delete("access_token");
|
|
1616
|
+
redirectUrlObj.searchParams.delete("refresh_token");
|
|
1617
|
+
redirectUrlObj.searchParams.delete("state");
|
|
1618
|
+
redirectUrlObj.searchParams.delete("error");
|
|
1619
|
+
redirectUrlObj.searchParams.delete("error_description");
|
|
1620
|
+
redirectUrlObj.hash = "";
|
|
1621
|
+
redirectUrl = redirectUrlObj.toString();
|
|
1622
|
+
} catch (e) {
|
|
1623
|
+
console.warn("Failed to clean redirect URL:", e);
|
|
1624
|
+
}
|
|
1625
|
+
try {
|
|
1626
|
+
if (typeof window !== "undefined") {
|
|
1627
|
+
sessionStorage.setItem("blink_oauth_state", state);
|
|
1628
|
+
sessionStorage.setItem("blink_oauth_redirect_url", redirectUrl);
|
|
1629
|
+
console.log("\u{1F4BE} Stored OAuth state:", { state, redirectUrl });
|
|
1630
|
+
}
|
|
1631
|
+
} catch (e) {
|
|
1632
|
+
console.warn("Failed to store OAuth state in sessionStorage:", e);
|
|
1633
|
+
}
|
|
1634
|
+
const isSafari = this.isSafari();
|
|
1635
|
+
const forceRedirect = options?.forceRedirect === true;
|
|
1636
|
+
if ((isSafari || forceRedirect) && typeof window !== "undefined") {
|
|
1637
|
+
console.log("\u{1F310} Using redirect flow (no popup)", {
|
|
1638
|
+
provider,
|
|
1639
|
+
projectId: this.config.projectId,
|
|
1640
|
+
state,
|
|
1641
|
+
redirectUrl,
|
|
1642
|
+
authUrl: this.authUrl,
|
|
1643
|
+
reason: isSafari ? "Safari detected" : "forceRedirect option set"
|
|
1644
|
+
});
|
|
1645
|
+
const authRedirectUrl = new URL("/auth", this.authUrl);
|
|
1646
|
+
authRedirectUrl.searchParams.set("provider", provider);
|
|
1647
|
+
authRedirectUrl.searchParams.set("project_id", this.config.projectId);
|
|
1648
|
+
authRedirectUrl.searchParams.set("state", state);
|
|
1649
|
+
authRedirectUrl.searchParams.set("mode", "redirect");
|
|
1650
|
+
authRedirectUrl.searchParams.set("redirect_url", redirectUrl);
|
|
1651
|
+
console.log("\u{1F504} Redirecting to:", authRedirectUrl.toString());
|
|
1652
|
+
window.location.href = authRedirectUrl.toString();
|
|
1653
|
+
return new Promise(() => {
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1656
|
+
return new Promise((resolve, reject) => {
|
|
1521
1657
|
const popupUrl = new URL("/auth", this.authUrl);
|
|
1522
1658
|
popupUrl.searchParams.set("provider", provider);
|
|
1523
1659
|
popupUrl.searchParams.set("project_id", this.config.projectId);
|
|
1524
1660
|
popupUrl.searchParams.set("state", state);
|
|
1525
1661
|
popupUrl.searchParams.set("mode", "popup");
|
|
1526
1662
|
popupUrl.searchParams.set("redirect_url", redirectUrl);
|
|
1663
|
+
if (typeof window !== "undefined") {
|
|
1664
|
+
popupUrl.searchParams.set("opener_origin", window.location.origin);
|
|
1665
|
+
}
|
|
1527
1666
|
const popup = window.open(
|
|
1528
1667
|
popupUrl.toString(),
|
|
1529
1668
|
"blink-auth",
|
|
1530
1669
|
"width=500,height=600,scrollbars=yes,resizable=yes"
|
|
1531
1670
|
);
|
|
1532
1671
|
if (!popup) {
|
|
1533
|
-
|
|
1672
|
+
console.warn("\u26A0\uFE0F Popup was blocked, falling back to redirect flow");
|
|
1673
|
+
const authRedirectUrl = new URL("/auth", this.authUrl);
|
|
1674
|
+
authRedirectUrl.searchParams.set("provider", provider);
|
|
1675
|
+
authRedirectUrl.searchParams.set("project_id", this.config.projectId);
|
|
1676
|
+
authRedirectUrl.searchParams.set("state", state);
|
|
1677
|
+
authRedirectUrl.searchParams.set("mode", "redirect");
|
|
1678
|
+
authRedirectUrl.searchParams.set("redirect_url", redirectUrl);
|
|
1679
|
+
console.log("\u{1F504} Falling back to redirect:", authRedirectUrl.toString());
|
|
1680
|
+
window.location.href = authRedirectUrl.toString();
|
|
1534
1681
|
return;
|
|
1535
1682
|
}
|
|
1683
|
+
try {
|
|
1684
|
+
if (popup.closed || !popup.window) {
|
|
1685
|
+
console.warn("\u26A0\uFE0F Popup appears to be blocked (closed immediately), falling back to redirect flow");
|
|
1686
|
+
const authRedirectUrl = new URL("/auth", this.authUrl);
|
|
1687
|
+
authRedirectUrl.searchParams.set("provider", provider);
|
|
1688
|
+
authRedirectUrl.searchParams.set("project_id", this.config.projectId);
|
|
1689
|
+
authRedirectUrl.searchParams.set("state", state);
|
|
1690
|
+
authRedirectUrl.searchParams.set("mode", "redirect");
|
|
1691
|
+
authRedirectUrl.searchParams.set("redirect_url", redirectUrl);
|
|
1692
|
+
console.log("\u{1F504} Falling back to redirect:", authRedirectUrl.toString());
|
|
1693
|
+
window.location.href = authRedirectUrl.toString();
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
} catch (e) {
|
|
1697
|
+
console.log("\u26A0\uFE0F Could not verify popup state, assuming it opened successfully");
|
|
1698
|
+
}
|
|
1536
1699
|
let timeoutId;
|
|
1537
1700
|
const messageListener = (event) => {
|
|
1538
1701
|
let allowed = false;
|
|
@@ -1542,7 +1705,12 @@ var BlinkAuth = class {
|
|
|
1542
1705
|
} catch {
|
|
1543
1706
|
}
|
|
1544
1707
|
if (event.origin === "http://localhost:3000" || event.origin === "http://localhost:3001") allowed = true;
|
|
1545
|
-
if (
|
|
1708
|
+
if (typeof window !== "undefined" && event.origin === window.location.origin) allowed = true;
|
|
1709
|
+
if (!allowed) {
|
|
1710
|
+
console.log("\u{1F6AB} Blocked postMessage from untrusted origin:", event.origin);
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
console.log("\u2705 Accepted postMessage from origin:", event.origin);
|
|
1546
1714
|
if (event.data?.type === "BLINK_AUTH_TOKENS") {
|
|
1547
1715
|
const { access_token, refresh_token, token_type, expires_in, refresh_expires_in, projectId, state: returnedState } = event.data;
|
|
1548
1716
|
try {
|
|
@@ -2302,17 +2470,50 @@ var BlinkAuth = class {
|
|
|
2302
2470
|
return null;
|
|
2303
2471
|
}
|
|
2304
2472
|
}
|
|
2473
|
+
/**
|
|
2474
|
+
* Extract URL parameters from both search params and hash fragments
|
|
2475
|
+
* Safari OAuth redirects often use hash fragments instead of query params
|
|
2476
|
+
*/
|
|
2477
|
+
extractUrlParams() {
|
|
2478
|
+
const params = new URLSearchParams();
|
|
2479
|
+
if (typeof window !== "undefined" && window.location.search) {
|
|
2480
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
2481
|
+
for (const [key, value] of searchParams.entries()) {
|
|
2482
|
+
params.set(key, value);
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
if (typeof window !== "undefined" && window.location.hash) {
|
|
2486
|
+
const hash = window.location.hash.substring(1);
|
|
2487
|
+
const hashParams = new URLSearchParams(hash);
|
|
2488
|
+
for (const [key, value] of hashParams.entries()) {
|
|
2489
|
+
if (!params.has(key)) {
|
|
2490
|
+
params.set(key, value);
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
return params;
|
|
2495
|
+
}
|
|
2305
2496
|
extractTokensFromUrl() {
|
|
2306
2497
|
if (typeof window === "undefined") return null;
|
|
2307
|
-
const params =
|
|
2498
|
+
const params = this.extractUrlParams();
|
|
2308
2499
|
const accessToken = params.get("access_token");
|
|
2309
2500
|
const refreshToken = params.get("refresh_token");
|
|
2501
|
+
const state = params.get("state");
|
|
2502
|
+
const error = params.get("error");
|
|
2310
2503
|
console.log("\u{1F50D} Extracting tokens from URL:", {
|
|
2311
2504
|
url: window.location.href,
|
|
2505
|
+
search: window.location.search,
|
|
2506
|
+
hash: window.location.hash,
|
|
2312
2507
|
accessToken: accessToken ? `${accessToken.substring(0, 20)}...` : null,
|
|
2313
2508
|
refreshToken: refreshToken ? `${refreshToken.substring(0, 20)}...` : null,
|
|
2509
|
+
state: state ? `${state.substring(0, 10)}...` : null,
|
|
2510
|
+
error: error || null,
|
|
2314
2511
|
allParams: Object.fromEntries(params.entries())
|
|
2315
2512
|
});
|
|
2513
|
+
if (error) {
|
|
2514
|
+
console.log("\u274C Error parameter found in URL, not extracting tokens");
|
|
2515
|
+
return null;
|
|
2516
|
+
}
|
|
2316
2517
|
if (accessToken) {
|
|
2317
2518
|
const tokens = {
|
|
2318
2519
|
access_token: accessToken,
|
|
@@ -2327,7 +2528,8 @@ var BlinkAuth = class {
|
|
|
2327
2528
|
};
|
|
2328
2529
|
console.log("\u2705 Tokens extracted successfully:", {
|
|
2329
2530
|
hasAccessToken: !!tokens.access_token,
|
|
2330
|
-
hasRefreshToken: !!tokens.refresh_token
|
|
2531
|
+
hasRefreshToken: !!tokens.refresh_token,
|
|
2532
|
+
state: state || "no state"
|
|
2331
2533
|
});
|
|
2332
2534
|
return tokens;
|
|
2333
2535
|
}
|
|
@@ -2347,8 +2549,9 @@ var BlinkAuth = class {
|
|
|
2347
2549
|
url.searchParams.delete("code");
|
|
2348
2550
|
url.searchParams.delete("error");
|
|
2349
2551
|
url.searchParams.delete("error_description");
|
|
2552
|
+
url.hash = "";
|
|
2350
2553
|
window.history.replaceState({}, "", url.toString());
|
|
2351
|
-
console.log("\u{1F9F9} URL cleaned up, removed auth parameters");
|
|
2554
|
+
console.log("\u{1F9F9} URL cleaned up, removed auth parameters from both search and hash");
|
|
2352
2555
|
}
|
|
2353
2556
|
redirectToAuth() {
|
|
2354
2557
|
if (typeof window !== "undefined") {
|
package/package.json
CHANGED