@hfunlabs/hypurr-connect 0.1.22 → 0.1.23
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 +17 -11
- package/dist/index.d.ts +3 -1
- package/dist/index.js +254 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/HypurrConnectProvider.tsx +326 -65
- package/src/types.ts +3 -1
package/README.md
CHANGED
|
@@ -44,11 +44,12 @@ pnpm add @hfunlabs/hyperliquid @protobuf-ts/grpcweb-transport @protobuf-ts/runti
|
|
|
44
44
|
import { HypurrConnectProvider } from "@hfunlabs/hypurr-connect";
|
|
45
45
|
|
|
46
46
|
const config = {
|
|
47
|
-
|
|
47
|
+
clientId: "your-client-id",
|
|
48
48
|
isTestnet: false,
|
|
49
49
|
grpcUrl: "https://grpc.hypurr.fun",
|
|
50
50
|
telegram: {
|
|
51
51
|
authHubUrl: "https://auth.hypurr.fun/login",
|
|
52
|
+
// tokenUrl defaults to https://auth.hypurr.fun/token
|
|
52
53
|
scope: [
|
|
53
54
|
"telegram:user:read",
|
|
54
55
|
"telegram:wallet:read",
|
|
@@ -119,13 +120,14 @@ function AppShell() {
|
|
|
119
120
|
|
|
120
121
|
```typescript
|
|
121
122
|
interface HypurrConnectConfig {
|
|
122
|
-
|
|
123
|
+
clientId: string; // Auth hub client identifier
|
|
123
124
|
grpcUrl?: string; // gRPC-web base URL (default: https://grpc.hypurr.fun)
|
|
124
125
|
mediaUrl?: string; // Media base URL (default: https://media.hypurr.fun)
|
|
125
126
|
grpcTimeout?: number; // Request timeout in ms (default: 15000)
|
|
126
127
|
isTestnet?: boolean; // Use testnet endpoints (default: false)
|
|
127
128
|
telegram: {
|
|
128
129
|
authHubUrl?: string; // Auth hub URL (default: https://auth.hypurr.fun/login)
|
|
130
|
+
tokenUrl?: string; // Token exchange URL (default: auth hub /token)
|
|
129
131
|
returnTo?: string | (() => string); // Callback URL (default: current page)
|
|
130
132
|
scope?: string | string[]; // Requested JWT scopes
|
|
131
133
|
};
|
|
@@ -134,7 +136,8 @@ interface HypurrConnectConfig {
|
|
|
134
136
|
|
|
135
137
|
The SDK no longer renders Telegram's login widget or opens `oauth.telegram.org`
|
|
136
138
|
directly. Telegram login is delegated to the Hypurr auth hub in a popup, and the
|
|
137
|
-
popup posts
|
|
139
|
+
popup posts either a legacy scoped JWT or an authorization code back to the
|
|
140
|
+
original page.
|
|
138
141
|
|
|
139
142
|
### Dependencies
|
|
140
143
|
|
|
@@ -147,16 +150,19 @@ for generated protobuf service clients.
|
|
|
147
150
|
|
|
148
151
|
1. User clicks "Telegram" in the `LoginModal`.
|
|
149
152
|
2. The SDK opens the configured auth hub in a popup with `client_id`,
|
|
150
|
-
`return_to`, `state`,
|
|
153
|
+
`return_to`, `state`, requested `scope`, `code_challenge`, and
|
|
154
|
+
`code_challenge_method=S256`.
|
|
151
155
|
3. The auth hub performs Telegram login and redirects back to `return_to` with
|
|
152
|
-
a scoped JWT.
|
|
153
|
-
4. The popup callback page posts `{ token, state }` to the
|
|
154
|
-
`postMessage` and closes.
|
|
155
|
-
5. The opener validates `state
|
|
156
|
-
|
|
157
|
-
6.
|
|
156
|
+
either a legacy scoped JWT or an authorization code.
|
|
157
|
+
4. The popup callback page posts `{ token, state }` or `{ code, state }` to the
|
|
158
|
+
opener with `postMessage` and closes.
|
|
159
|
+
5. The opener validates `state`. Legacy tokens are stored directly; auth codes
|
|
160
|
+
are exchanged at the configured token URL with the stored PKCE verifier.
|
|
161
|
+
6. The SDK calls the Hypurr gRPC backend with `Authorization: Bearer <jwt>`
|
|
162
|
+
metadata.
|
|
163
|
+
7. An `ExchangeClient` is created with `GrpcExchangeTransport`; exchange
|
|
158
164
|
actions are still signed server-side by the Hypurr backend.
|
|
159
|
-
|
|
165
|
+
8. The JWT session is persisted in localStorage (`hypurr-connect-tg-jwt`).
|
|
160
166
|
|
|
161
167
|
If the popup is blocked, the SDK falls back to a full-page redirect. The
|
|
162
168
|
default `returnTo` is the current page with auth query params removed; custom
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { AbstractViemLocalAccount } from '@hfunlabs/hyperliquid/signing';
|
|
|
15
15
|
|
|
16
16
|
interface HypurrConnectConfig {
|
|
17
17
|
/** Auth hub client identifier. Sent as `client_id` during Telegram login. */
|
|
18
|
-
|
|
18
|
+
clientId: string;
|
|
19
19
|
/** gRPC-web base URL. Defaults to https://grpc.hypurr.fun. */
|
|
20
20
|
grpcUrl?: string;
|
|
21
21
|
/** Media base URL for user-uploaded assets. Defaults to https://media.hypurr.fun. */
|
|
@@ -27,6 +27,8 @@ interface HypurrConnectConfig {
|
|
|
27
27
|
telegram?: {
|
|
28
28
|
/** Auth hub login URL. Defaults to https://auth.hypurr.fun/login. */
|
|
29
29
|
authHubUrl?: string;
|
|
30
|
+
/** Auth hub token exchange URL. Defaults to the auth hub login URL with `/login` replaced by `/token`. */
|
|
31
|
+
tokenUrl?: string;
|
|
30
32
|
/** Optional callback URL. Defaults to the current page without auth query params. */
|
|
31
33
|
returnTo?: string | (() => string);
|
|
32
34
|
/** Requested hub scopes. Defaults to the scopes required by this SDK. */
|
package/dist/index.js
CHANGED
|
@@ -295,6 +295,8 @@ import { jsx } from "react/jsx-runtime";
|
|
|
295
295
|
var TELEGRAM_STORAGE_KEY = "hypurr-connect-tg-jwt";
|
|
296
296
|
var LEGACY_TELEGRAM_STORAGE_KEY = "hypurr-connect-tg-user";
|
|
297
297
|
var TELEGRAM_AUTH_STATE_KEY = "hypurr-connect-auth-state";
|
|
298
|
+
var TELEGRAM_AUTH_CODE_VERIFIER_PREFIX = "hypurr-connect-auth-code-verifier:";
|
|
299
|
+
var TELEGRAM_AUTH_RETURN_TO_PREFIX = "hypurr-connect-auth-return-to:";
|
|
298
300
|
var TELEGRAM_AUTH_MESSAGE = "hypurr-connect:telegram-auth";
|
|
299
301
|
var DEFAULT_AUTH_HUB_URL = "https://auth.hypurr.fun/login";
|
|
300
302
|
var DEFAULT_MEDIA_URL = "https://media.hypurr.fun";
|
|
@@ -430,6 +432,9 @@ function withExpectedFrom(transaction, address) {
|
|
|
430
432
|
function currentReturnTo() {
|
|
431
433
|
const url = new URL(window.location.href);
|
|
432
434
|
for (const param of [
|
|
435
|
+
"code",
|
|
436
|
+
"error",
|
|
437
|
+
"error_description",
|
|
433
438
|
"token",
|
|
434
439
|
"token_type",
|
|
435
440
|
"token_source",
|
|
@@ -440,6 +445,29 @@ function currentReturnTo() {
|
|
|
440
445
|
}
|
|
441
446
|
return url.toString();
|
|
442
447
|
}
|
|
448
|
+
function cleanAuthCallbackUrl() {
|
|
449
|
+
const cleanUrl = new URL(window.location.href);
|
|
450
|
+
for (const param of [
|
|
451
|
+
"code",
|
|
452
|
+
"error",
|
|
453
|
+
"error_description",
|
|
454
|
+
"token",
|
|
455
|
+
"token_type",
|
|
456
|
+
"token_source",
|
|
457
|
+
"state",
|
|
458
|
+
"scope"
|
|
459
|
+
]) {
|
|
460
|
+
cleanUrl.searchParams.delete(param);
|
|
461
|
+
}
|
|
462
|
+
window.history.replaceState({}, document.title, cleanUrl.toString());
|
|
463
|
+
}
|
|
464
|
+
function base64UrlEncode(bytes) {
|
|
465
|
+
let value = "";
|
|
466
|
+
for (const byte of bytes) {
|
|
467
|
+
value += String.fromCharCode(byte);
|
|
468
|
+
}
|
|
469
|
+
return btoa(value).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
470
|
+
}
|
|
443
471
|
function randomState() {
|
|
444
472
|
const bytes = new Uint8Array(16);
|
|
445
473
|
crypto.getRandomValues(bytes);
|
|
@@ -447,6 +475,23 @@ function randomState() {
|
|
|
447
475
|
""
|
|
448
476
|
);
|
|
449
477
|
}
|
|
478
|
+
function randomCodeVerifier() {
|
|
479
|
+
const bytes = new Uint8Array(64);
|
|
480
|
+
crypto.getRandomValues(bytes);
|
|
481
|
+
return base64UrlEncode(bytes);
|
|
482
|
+
}
|
|
483
|
+
async function createCodeChallenge(codeVerifier) {
|
|
484
|
+
if (!crypto.subtle) {
|
|
485
|
+
throw new Error(
|
|
486
|
+
"[HypurrConnect] Web Crypto API is required for Telegram auth."
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
const digest = await crypto.subtle.digest(
|
|
490
|
+
"SHA-256",
|
|
491
|
+
new TextEncoder().encode(codeVerifier)
|
|
492
|
+
);
|
|
493
|
+
return base64UrlEncode(new Uint8Array(digest));
|
|
494
|
+
}
|
|
450
495
|
function normalizeScopes(scope) {
|
|
451
496
|
if (Array.isArray(scope)) return scope.join(" ");
|
|
452
497
|
return scope?.trim() || DEFAULT_TELEGRAM_SCOPES.join(" ");
|
|
@@ -454,12 +499,124 @@ function normalizeScopes(scope) {
|
|
|
454
499
|
function normalizeClientId(clientId) {
|
|
455
500
|
const normalized = typeof clientId === "string" ? clientId.trim() : "";
|
|
456
501
|
if (!normalized) {
|
|
457
|
-
throw new Error("[HypurrConnect] config.
|
|
502
|
+
throw new Error("[HypurrConnect] config.clientId is required.");
|
|
458
503
|
}
|
|
459
504
|
return normalized;
|
|
460
505
|
}
|
|
506
|
+
function codeVerifierStorageKey(state) {
|
|
507
|
+
return `${TELEGRAM_AUTH_CODE_VERIFIER_PREFIX}${state}`;
|
|
508
|
+
}
|
|
509
|
+
function returnToStorageKey(state) {
|
|
510
|
+
return `${TELEGRAM_AUTH_RETURN_TO_PREFIX}${state}`;
|
|
511
|
+
}
|
|
512
|
+
function storeTelegramAuthSession(state, codeVerifier, returnTo) {
|
|
513
|
+
const previousState = sessionStorage.getItem(TELEGRAM_AUTH_STATE_KEY);
|
|
514
|
+
if (previousState) {
|
|
515
|
+
sessionStorage.removeItem(codeVerifierStorageKey(previousState));
|
|
516
|
+
sessionStorage.removeItem(returnToStorageKey(previousState));
|
|
517
|
+
}
|
|
518
|
+
sessionStorage.setItem(TELEGRAM_AUTH_STATE_KEY, state);
|
|
519
|
+
sessionStorage.setItem(codeVerifierStorageKey(state), codeVerifier);
|
|
520
|
+
sessionStorage.setItem(returnToStorageKey(state), returnTo);
|
|
521
|
+
}
|
|
522
|
+
function clearTelegramAuthSession(state) {
|
|
523
|
+
sessionStorage.removeItem(TELEGRAM_AUTH_STATE_KEY);
|
|
524
|
+
sessionStorage.removeItem(codeVerifierStorageKey(state));
|
|
525
|
+
sessionStorage.removeItem(returnToStorageKey(state));
|
|
526
|
+
}
|
|
527
|
+
function takeTelegramAuthSession(state) {
|
|
528
|
+
const expectedState = sessionStorage.getItem(TELEGRAM_AUTH_STATE_KEY);
|
|
529
|
+
sessionStorage.removeItem(TELEGRAM_AUTH_STATE_KEY);
|
|
530
|
+
if (!expectedState || state !== expectedState) {
|
|
531
|
+
if (expectedState) {
|
|
532
|
+
sessionStorage.removeItem(codeVerifierStorageKey(expectedState));
|
|
533
|
+
sessionStorage.removeItem(returnToStorageKey(expectedState));
|
|
534
|
+
}
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
const codeVerifierKey = codeVerifierStorageKey(state);
|
|
538
|
+
const returnToKey = returnToStorageKey(state);
|
|
539
|
+
const codeVerifier = sessionStorage.getItem(codeVerifierKey);
|
|
540
|
+
const returnTo = sessionStorage.getItem(returnToKey);
|
|
541
|
+
sessionStorage.removeItem(codeVerifierKey);
|
|
542
|
+
sessionStorage.removeItem(returnToKey);
|
|
543
|
+
return { codeVerifier, returnTo };
|
|
544
|
+
}
|
|
545
|
+
function resolveAuthTokenUrl(authHubUrl, tokenUrl) {
|
|
546
|
+
const configuredTokenUrl = tokenUrl?.trim();
|
|
547
|
+
if (configuredTokenUrl) return configuredTokenUrl;
|
|
548
|
+
const url = new URL(authHubUrl || DEFAULT_AUTH_HUB_URL);
|
|
549
|
+
const pathWithoutTrailingSlash = url.pathname.replace(/\/+$/, "");
|
|
550
|
+
const basePath = pathWithoutTrailingSlash.replace(/\/[^/]*$/, "");
|
|
551
|
+
url.pathname = `${basePath}/token`;
|
|
552
|
+
url.search = "";
|
|
553
|
+
url.hash = "";
|
|
554
|
+
return url.toString();
|
|
555
|
+
}
|
|
556
|
+
function getTokenFromExchangeResponse(data) {
|
|
557
|
+
if (typeof data === "string") {
|
|
558
|
+
const token = data.trim();
|
|
559
|
+
return token || null;
|
|
560
|
+
}
|
|
561
|
+
if (typeof data !== "object" || data === null) return null;
|
|
562
|
+
const response = data;
|
|
563
|
+
for (const token of [response.token, response.access_token, response.jwt]) {
|
|
564
|
+
if (typeof token === "string" && token.trim()) return token.trim();
|
|
565
|
+
}
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
async function exchangeTelegramAuthCode({
|
|
569
|
+
authHubUrl,
|
|
570
|
+
clientId,
|
|
571
|
+
code,
|
|
572
|
+
codeVerifier,
|
|
573
|
+
returnTo,
|
|
574
|
+
tokenUrl
|
|
575
|
+
}) {
|
|
576
|
+
const body = new URLSearchParams({
|
|
577
|
+
client_id: clientId,
|
|
578
|
+
code,
|
|
579
|
+
code_verifier: codeVerifier,
|
|
580
|
+
grant_type: "authorization_code",
|
|
581
|
+
return_to: returnTo
|
|
582
|
+
});
|
|
583
|
+
const response = await fetch(resolveAuthTokenUrl(authHubUrl, tokenUrl), {
|
|
584
|
+
method: "POST",
|
|
585
|
+
headers: {
|
|
586
|
+
accept: "application/json",
|
|
587
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
588
|
+
},
|
|
589
|
+
body
|
|
590
|
+
});
|
|
591
|
+
const responseText = await response.text();
|
|
592
|
+
if (!response.ok) {
|
|
593
|
+
const detail = responseText.trim();
|
|
594
|
+
throw new Error(
|
|
595
|
+
detail ? `[HypurrConnect] Auth code exchange failed: ${detail}` : `[HypurrConnect] Auth code exchange failed with HTTP ${response.status}.`
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
let responseData = responseText;
|
|
599
|
+
if (responseText) {
|
|
600
|
+
try {
|
|
601
|
+
responseData = JSON.parse(responseText);
|
|
602
|
+
} catch {
|
|
603
|
+
responseData = responseText;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
const token = getTokenFromExchangeResponse(responseData);
|
|
607
|
+
if (!token) {
|
|
608
|
+
throw new Error("[HypurrConnect] Auth code exchange did not return a JWT.");
|
|
609
|
+
}
|
|
610
|
+
return token;
|
|
611
|
+
}
|
|
461
612
|
function isTelegramAuthMessage(data) {
|
|
462
|
-
|
|
613
|
+
if (typeof data !== "object" || data === null) return false;
|
|
614
|
+
if (!("type" in data) || !("state" in data)) return false;
|
|
615
|
+
const message = data;
|
|
616
|
+
const hasToken = typeof message.token === "string";
|
|
617
|
+
const hasCode = typeof message.code === "string";
|
|
618
|
+
const hasError = typeof message.error === "string";
|
|
619
|
+
return message.type === TELEGRAM_AUTH_MESSAGE && typeof message.state === "string" && (hasToken || hasCode || hasError);
|
|
463
620
|
}
|
|
464
621
|
var HypurrConnectContext = createContext(null);
|
|
465
622
|
function useHypurrConnect() {
|
|
@@ -517,6 +674,49 @@ function HypurrConnectProvider({
|
|
|
517
674
|
localStorage.setItem(TELEGRAM_STORAGE_KEY, token);
|
|
518
675
|
localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
|
|
519
676
|
}, []);
|
|
677
|
+
const handleTelegramAuthCallback = useCallback(
|
|
678
|
+
(callback) => {
|
|
679
|
+
const authSession = takeTelegramAuthSession(callback.state);
|
|
680
|
+
if (!authSession) {
|
|
681
|
+
setTgError("Invalid auth callback state.");
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (callback.error) {
|
|
685
|
+
setTgError(callback.error);
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
if (callback.code) {
|
|
689
|
+
if (!authSession.codeVerifier) {
|
|
690
|
+
setTgError("Missing auth code verifier.");
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
setTgLoading(true);
|
|
694
|
+
setTgError(null);
|
|
695
|
+
void exchangeTelegramAuthCode({
|
|
696
|
+
authHubUrl: config.telegram?.authHubUrl,
|
|
697
|
+
clientId: normalizeClientId(config.clientId),
|
|
698
|
+
code: callback.code,
|
|
699
|
+
codeVerifier: authSession.codeVerifier,
|
|
700
|
+
returnTo: authSession.returnTo || currentReturnTo(),
|
|
701
|
+
tokenUrl: config.telegram?.tokenUrl
|
|
702
|
+
}).then(acceptTelegramToken).catch(
|
|
703
|
+
(err) => setTgError(err instanceof Error ? err.message : String(err))
|
|
704
|
+
).finally(() => setTgLoading(false));
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
if (callback.token) {
|
|
708
|
+
acceptTelegramToken(callback.token);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
setTgError("Invalid auth callback.");
|
|
712
|
+
},
|
|
713
|
+
[
|
|
714
|
+
acceptTelegramToken,
|
|
715
|
+
config.clientId,
|
|
716
|
+
config.telegram?.authHubUrl,
|
|
717
|
+
config.telegram?.tokenUrl
|
|
718
|
+
]
|
|
719
|
+
);
|
|
520
720
|
useEffect(() => {
|
|
521
721
|
if (typeof document === "undefined" || !document.fonts) return;
|
|
522
722
|
for (const face of [
|
|
@@ -532,57 +732,37 @@ function HypurrConnectProvider({
|
|
|
532
732
|
useEffect(() => {
|
|
533
733
|
const params = new URLSearchParams(window.location.search);
|
|
534
734
|
const token = params.get("token");
|
|
535
|
-
|
|
735
|
+
const code = params.get("code");
|
|
736
|
+
const error = params.get("error_description") || params.get("error") || void 0;
|
|
737
|
+
if (!token && !code && !error) {
|
|
536
738
|
localStorage.removeItem(LEGACY_TELEGRAM_STORAGE_KEY);
|
|
537
739
|
return;
|
|
538
740
|
}
|
|
539
741
|
const callbackState = params.get("state") ?? "";
|
|
742
|
+
const callback = {
|
|
743
|
+
code: code || void 0,
|
|
744
|
+
error,
|
|
745
|
+
state: callbackState,
|
|
746
|
+
token: token || void 0,
|
|
747
|
+
type: TELEGRAM_AUTH_MESSAGE
|
|
748
|
+
};
|
|
540
749
|
if (window.opener && window.opener !== window) {
|
|
541
|
-
window.opener.postMessage(
|
|
542
|
-
{
|
|
543
|
-
type: TELEGRAM_AUTH_MESSAGE,
|
|
544
|
-
token,
|
|
545
|
-
state: callbackState
|
|
546
|
-
},
|
|
547
|
-
window.location.origin
|
|
548
|
-
);
|
|
750
|
+
window.opener.postMessage(callback, window.location.origin);
|
|
549
751
|
window.close();
|
|
550
752
|
return;
|
|
551
753
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
setTgError("Invalid auth callback state.");
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
acceptTelegramToken(token);
|
|
559
|
-
const cleanUrl = new URL(window.location.href);
|
|
560
|
-
for (const param of [
|
|
561
|
-
"token",
|
|
562
|
-
"token_type",
|
|
563
|
-
"token_source",
|
|
564
|
-
"state",
|
|
565
|
-
"scope"
|
|
566
|
-
]) {
|
|
567
|
-
cleanUrl.searchParams.delete(param);
|
|
568
|
-
}
|
|
569
|
-
window.history.replaceState({}, document.title, cleanUrl.toString());
|
|
570
|
-
}, [acceptTelegramToken]);
|
|
754
|
+
cleanAuthCallbackUrl();
|
|
755
|
+
handleTelegramAuthCallback(callback);
|
|
756
|
+
}, [handleTelegramAuthCallback]);
|
|
571
757
|
useEffect(() => {
|
|
572
758
|
function onMessage(event) {
|
|
573
759
|
if (event.origin !== window.location.origin) return;
|
|
574
760
|
if (!isTelegramAuthMessage(event.data)) return;
|
|
575
|
-
|
|
576
|
-
sessionStorage.removeItem(TELEGRAM_AUTH_STATE_KEY);
|
|
577
|
-
if (!expectedState || event.data.state !== expectedState) {
|
|
578
|
-
setTgError("Invalid auth callback state.");
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
acceptTelegramToken(event.data.token);
|
|
761
|
+
handleTelegramAuthCallback(event.data);
|
|
582
762
|
}
|
|
583
763
|
window.addEventListener("message", onMessage);
|
|
584
764
|
return () => window.removeEventListener("message", onMessage);
|
|
585
|
-
}, [
|
|
765
|
+
}, [handleTelegramAuthCallback]);
|
|
586
766
|
useEffect(() => {
|
|
587
767
|
if (!tgAuthToken || !telegramRpcOptions) return;
|
|
588
768
|
let cancelled = false;
|
|
@@ -1317,22 +1497,16 @@ function HypurrConnectProvider({
|
|
|
1317
1497
|
const closeLoginModal = useCallback(() => setLoginModalOpen(false), []);
|
|
1318
1498
|
const loginTelegram = useCallback(() => {
|
|
1319
1499
|
const state = randomState();
|
|
1320
|
-
|
|
1500
|
+
const codeVerifier = randomCodeVerifier();
|
|
1321
1501
|
const configuredReturnTo = config.telegram?.returnTo;
|
|
1322
1502
|
const returnTo = typeof configuredReturnTo === "function" ? configuredReturnTo() : configuredReturnTo || currentReturnTo();
|
|
1323
|
-
|
|
1324
|
-
config.telegram?.authHubUrl || DEFAULT_AUTH_HUB_URL
|
|
1325
|
-
);
|
|
1326
|
-
authUrl.searchParams.set("client_id", normalizeClientId(config.client_id));
|
|
1327
|
-
authUrl.searchParams.set("return_to", returnTo);
|
|
1328
|
-
authUrl.searchParams.set("state", state);
|
|
1329
|
-
authUrl.searchParams.set("scope", normalizeScopes(config.telegram?.scope));
|
|
1503
|
+
storeTelegramAuthSession(state, codeVerifier, returnTo);
|
|
1330
1504
|
const width = 520;
|
|
1331
1505
|
const height = 720;
|
|
1332
1506
|
const left = window.screenX + Math.max(0, (window.outerWidth - width) / 2);
|
|
1333
1507
|
const top = window.screenY + Math.max(0, (window.outerHeight - height) / 2);
|
|
1334
1508
|
const popup = window.open(
|
|
1335
|
-
|
|
1509
|
+
"about:blank",
|
|
1336
1510
|
"hypurr_telegram_auth",
|
|
1337
1511
|
[
|
|
1338
1512
|
`width=${width}`,
|
|
@@ -1343,13 +1517,40 @@ function HypurrConnectProvider({
|
|
|
1343
1517
|
"scrollbars=yes"
|
|
1344
1518
|
].join(",")
|
|
1345
1519
|
);
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1520
|
+
void (async () => {
|
|
1521
|
+
try {
|
|
1522
|
+
const authUrl = new URL(
|
|
1523
|
+
config.telegram?.authHubUrl || DEFAULT_AUTH_HUB_URL
|
|
1524
|
+
);
|
|
1525
|
+
authUrl.searchParams.set(
|
|
1526
|
+
"client_id",
|
|
1527
|
+
normalizeClientId(config.clientId)
|
|
1528
|
+
);
|
|
1529
|
+
authUrl.searchParams.set("return_to", returnTo);
|
|
1530
|
+
authUrl.searchParams.set("state", state);
|
|
1531
|
+
authUrl.searchParams.set(
|
|
1532
|
+
"scope",
|
|
1533
|
+
normalizeScopes(config.telegram?.scope)
|
|
1534
|
+
);
|
|
1535
|
+
authUrl.searchParams.set(
|
|
1536
|
+
"code_challenge",
|
|
1537
|
+
await createCodeChallenge(codeVerifier)
|
|
1538
|
+
);
|
|
1539
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
1540
|
+
if (popup) {
|
|
1541
|
+
popup.location.assign(authUrl.toString());
|
|
1542
|
+
popup.focus();
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
window.location.assign(authUrl.toString());
|
|
1546
|
+
} catch (err) {
|
|
1547
|
+
clearTelegramAuthSession(state);
|
|
1548
|
+
if (popup && !popup.closed) popup.close();
|
|
1549
|
+
setTgError(err instanceof Error ? err.message : String(err));
|
|
1550
|
+
}
|
|
1551
|
+
})();
|
|
1351
1552
|
}, [
|
|
1352
|
-
config.
|
|
1553
|
+
config.clientId,
|
|
1353
1554
|
config.telegram?.authHubUrl,
|
|
1354
1555
|
config.telegram?.returnTo,
|
|
1355
1556
|
config.telegram?.scope
|