@openai-oauth/react 2.0.0-beta.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 ADDED
@@ -0,0 +1,143 @@
1
+ # @openai-oauth/react
2
+
3
+ [Docs](https://github.com/EvanZhouDev/openai-oauth#react-component) | [GitHub](https://github.com/EvanZhouDev/openai-oauth) | [npm](https://www.npmjs.com/package/@openai-oauth/react)
4
+
5
+ Let your users sign in with their ChatGPT accounts.
6
+
7
+ ```bash
8
+ npm i @openai-oauth/react
9
+ ```
10
+
11
+ Quickstart:
12
+
13
+ ```tsx
14
+ "use client";
15
+
16
+ import { SignInWithChatGPT } from "@openai-oauth/react";
17
+
18
+ export default function Page() {
19
+ return <SignInWithChatGPT />;
20
+ }
21
+ ```
22
+
23
+ ## Package Notes
24
+
25
+ `SignInWithChatGPT` renders the OpenAI-style sign-in button. After sign-in, it becomes a disconnect button.
26
+
27
+ The prebuilt button includes a small "Powered by OpenAI OAuth" link by default. Pass `hideAttribution` to render only the button with no attribution link or reserved space.
28
+
29
+ No app route is required for login. The hook creates the authorization URL, stores PKCE/state in `sessionStorage`, exchanges the callback code directly, and stores the resulting session in the browser session store.
30
+
31
+ Browser model calls must go through your own app route because ChatGPT does not allow direct browser CORS requests. Send the signed-in session to that route with `openaiAuthHeaders()`:
32
+
33
+ ```tsx
34
+ "use client";
35
+
36
+ import { openaiAuthHeaders, SignInWithChatGPT } from "@openai-oauth/react";
37
+
38
+ async function ask() {
39
+ const response = await fetch("/api/chat", {
40
+ method: "POST",
41
+ headers: await openaiAuthHeaders({
42
+ headers: { "content-type": "application/json" },
43
+ }),
44
+ body: JSON.stringify({ prompt: "Hello!" }),
45
+ });
46
+
47
+ return response.text();
48
+ }
49
+ ```
50
+
51
+ Read the request-bound credentials on your server:
52
+
53
+ ```ts
54
+ import { createOpenAIOAuth } from "@openai-oauth/ai-sdk";
55
+ import { openaiCredentials } from "@openai-oauth/react/server";
56
+ import { generateText } from "ai";
57
+
58
+ export async function POST(request: Request) {
59
+ const { prompt } = await request.json();
60
+ const openai = createOpenAIOAuth(openaiCredentials(request));
61
+
62
+ const result = await generateText({
63
+ model: openai("gpt-5.4-mini"),
64
+ prompt,
65
+ });
66
+
67
+ return Response.json({ text: result.text });
68
+ }
69
+ ```
70
+
71
+ Useful props:
72
+
73
+ ```tsx
74
+ <SignInWithChatGPT
75
+ onSuccess={(session) => console.log(session.accountId)}
76
+ onError={(error) => console.error(error.message)}
77
+ onStateChange={(state) => console.log(state.status)}
78
+ hideAttribution
79
+ />
80
+ ```
81
+
82
+ For custom UI, use the hook:
83
+
84
+ ```tsx
85
+ import { useSignInWithChatGPT } from "@openai-oauth/react";
86
+
87
+ function CustomLogin() {
88
+ const login = useSignInWithChatGPT();
89
+
90
+ if (login.status === "signed-in") {
91
+ return <button onClick={() => void login.logout()}>Disconnect</button>;
92
+ }
93
+
94
+ return (
95
+ <button onClick={() => void login.login()}>Sign in with ChatGPT</button>
96
+ );
97
+ }
98
+ ```
99
+
100
+ The hook returns:
101
+
102
+ ```ts
103
+ type UseSignInWithChatGPTReturn = SignInWithChatGPTState & {
104
+ isSignedIn: boolean;
105
+ login(): Promise<void>;
106
+ logout(): Promise<void>;
107
+ refresh(): Promise<OpenAIOAuthSession | null>;
108
+ reset(): void;
109
+ };
110
+ ```
111
+
112
+ State statuses:
113
+
114
+ ```ts
115
+ "checking" | "signed-out" | "starting" | "redirecting" | "signed-in" | "error"
116
+ ```
117
+
118
+ The default browser session store persists encrypted sessions in IndexedDB. Apps can provide their own store:
119
+
120
+ ```ts
121
+ import { createSessionStore } from "@openai-oauth/react";
122
+
123
+ const sessionStore = createSessionStore();
124
+
125
+ type SessionStore = {
126
+ get(): Promise<OpenAIOAuthSession | null>;
127
+ set(session: OpenAIOAuthSession): Promise<void>;
128
+ clear(): Promise<void>;
129
+ };
130
+ ```
131
+
132
+ Exports:
133
+
134
+ - `SignInWithChatGPT`
135
+ - `useSignInWithChatGPT`
136
+ - `openaiAuthHeaders`
137
+ - `getSession`
138
+ - `createSessionStore`
139
+ - `openaiCredentials` from `@openai-oauth/react/server`
140
+
141
+ ## More
142
+
143
+ [Learn more in the openai-oauth README.](https://github.com/EvanZhouDev/openai-oauth#react-component)
@@ -0,0 +1,12 @@
1
+ import type { ButtonHTMLAttributes } from "react";
2
+ import { type UseSignInWithChatGPTOptions } from "./useSignInWithChatGPT.js";
3
+ export type SignInWithChatGPTProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onError"> & UseSignInWithChatGPTOptions & {
4
+ loadingLabel?: string;
5
+ redirectingLabel?: string;
6
+ signedInLabel?: string;
7
+ hideAttribution?: boolean;
8
+ hideWhenSignedIn?: boolean;
9
+ showLogo?: boolean;
10
+ };
11
+ export declare const SignInWithChatGPT: ({ callbackPath, clientId, codeVerifier, sessionStore, extraParams, fetch, idTokenAddOrganizations, issuer, onSuccess, onError, onStateChange, openMode, redirectUri, scope, simplifiedFlow, state, loadingLabel, redirectingLabel, signedInLabel, hideAttribution, hideWhenSignedIn, showLogo, children, disabled, onClick, onMouseEnter, onMouseLeave, style, type, ...props }: SignInWithChatGPTProps) => import("react").JSX.Element | null;
12
+ //# sourceMappingURL=SignInWithChatGPT.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignInWithChatGPT.d.ts","sourceRoot":"","sources":["../src/SignInWithChatGPT.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,oBAAoB,EAIpB,MAAM,OAAO,CAAA;AAEd,OAAO,EACN,KAAK,2BAA2B,EAEhC,MAAM,2BAA2B,CAAA;AAElC,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACxC,oBAAoB,CAAC,iBAAiB,CAAC,EACvC,SAAS,CACT,GACA,2BAA2B,GAAG;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAkEF,eAAO,MAAM,iBAAiB,GAAI,iXA+B/B,sBAAsB,uCA6FxB,CAAA"}
@@ -0,0 +1,105 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useSignInWithChatGPT, } from "./useSignInWithChatGPT.js";
4
+ const containerStyle = {
5
+ alignItems: "center",
6
+ display: "inline-flex",
7
+ flexDirection: "column",
8
+ gap: 5,
9
+ };
10
+ const buttonStyle = {
11
+ alignItems: "center",
12
+ background: "#ffffff",
13
+ border: "1px solid #d9d9d9",
14
+ borderRadius: 9999,
15
+ color: "#111111",
16
+ cursor: "pointer",
17
+ display: "inline-flex",
18
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
19
+ fontSize: 15,
20
+ fontWeight: 450,
21
+ gap: 12,
22
+ justifyContent: "center",
23
+ lineHeight: 1.2,
24
+ minHeight: 52,
25
+ minWidth: 224,
26
+ padding: "14px 22px",
27
+ whiteSpace: "nowrap",
28
+ };
29
+ const logoStyle = {
30
+ display: "block",
31
+ flex: "0 0 auto",
32
+ height: 22,
33
+ width: 22,
34
+ };
35
+ const attributionStyle = {
36
+ color: "#8f8f8f",
37
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
38
+ fontSize: 10,
39
+ fontWeight: 400,
40
+ lineHeight: 1.2,
41
+ textDecoration: "underline",
42
+ textDecorationColor: "currentColor",
43
+ textUnderlineOffset: 2,
44
+ };
45
+ const OpenAILogo = () => (_jsxs("svg", { "aria-hidden": "true", fill: "none", focusable: "false", style: logoStyle, viewBox: "0 0 5086 5021", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("title", { children: "OpenAI" }), _jsx("path", { d: "M1947.45 1832.08V1357.04C1947.45 1317.03 1962.45 1287.01 1997.43 1267.05L2952.46 717.024C3082.47 642.035 3237.47 607.018 3397.45 607.018C3997.47 607.018 4377.48 1072.05 4377.48 1567.07C4377.48 1602.06 4377.48 1642.06 4372.46 1682.07L3382.45 1102.04C3322.46 1067.03 3262.46 1067.03 3202.45 1102.04L1947.45 1832.08ZM4177.46 3682.19V2547.08C4177.46 2477.08 4147.44 2427.07 4087.45 2392.08L2832.45 1662.05L3242.46 1427.01C3277.44 1407.02 3307.47 1407.02 3342.45 1427.01L4297.47 1977.03C4572.51 2137.05 4757.46 2477.08 4757.46 2807.06C4757.46 3187.06 4532.51 3537.1 4177.46 3682.12V3682.19ZM1652.43 2682.13L1242.42 2442.13C1207.44 2422.14 1192.44 2392.11 1192.44 2352.14V1252.09C1192.44 717.057 1602.45 312.018 2157.48 312.018C2367.47 312.018 2562.43 382.019 2727.5 507.022L1742.48 1077.07C1682.49 1112.05 1652.49 1162.07 1652.49 1232.1V2682.16L1652.43 2682.13ZM2534.98 3192.15L1947.38 2862.13V2162.13L2534.91 1832.11L3122.41 2162.13V2862.13L2534.98 3192.15ZM2912.45 4712.24C2702.43 4712.24 2507.46 4642.21 2342.43 4517.24L3327.42 3947.16C3387.41 3912.17 3417.43 3862.19 3417.43 3792.16V2342.07L3832.46 2582.06C3867.45 2602.05 3882.44 2632.08 3882.44 2672.08V3772.14C3882.44 4307.14 3467.41 4712.24 2912.45 4712.24ZM1727.41 3597.19L772.388 3047.16C497.352 2887.14 312.365 2547.15 312.365 2217.13C312.365 1832.11 542.409 1487.1 897.386 1342.07V2482.13C897.386 2552.17 927.38 2602.15 987.369 2637.13L2237.42 3362.15L1827.41 3597.19C1792.42 3617.17 1762.4 3617.17 1727.41 3597.19ZM1672.45 4417.24C1107.44 4417.24 692.414 3992.18 692.414 3467.16C692.414 3427.16 697.434 3387.15 702.422 3347.15L1687.41 3917.19C1747.42 3952.19 1807.42 3952.19 1867.41 3917.19L3122.41 3192.18V3667.22C3122.41 3707.22 3107.42 3737.22 3072.43 3757.21L2117.41 4307.23C1987.39 4382.22 1832.39 4417.24 1672.38 4417.24H1672.45ZM2912.45 5012.23C3517.46 5012.23 4022.44 4582.22 4137.49 4012.17C4697.48 3867.15 5057.51 3342.13 5057.51 2807.13C5057.51 2457.09 4907.5 2117.1 4637.49 1872.05C4662.49 1767.06 4677.52 1662.05 4677.52 1557.1C4677.52 842.06 4097.49 306.997 3427.48 306.997C3292.5 306.997 3162.48 326.951 3032.46 371.977C2807.44 151.966 2497.45 11.9648 2157.44 11.9648C1552.44 11.9648 1047.46 441.978 932.434 1012.02C372.419 1157.05 12.4219 1682.03 12.4219 2217.06C12.4219 2567.13 162.394 2907.13 432.408 3152.14C407.402 3257.13 392.405 3362.15 392.405 3467.13C392.405 4182.13 972.404 4717.2 1642.45 4717.2C1777.43 4717.2 1907.41 4697.24 2037.43 4652.22C2262.39 4872.23 2572.41 5012.23 2912.45 5012.23Z", fill: "currentColor" })] }));
46
+ export const SignInWithChatGPT = ({ callbackPath, clientId, codeVerifier, sessionStore, extraParams, fetch, idTokenAddOrganizations, issuer, onSuccess, onError, onStateChange, openMode, redirectUri, scope, simplifiedFlow, state, loadingLabel = "Connecting...", redirectingLabel = "Signing in...", signedInLabel = "Disconnect ChatGPT", hideAttribution = false, hideWhenSignedIn = false, showLogo = true, children = "Sign in with ChatGPT", disabled, onClick, onMouseEnter, onMouseLeave, style, type = "button", ...props }) => {
47
+ const [isHovered, setIsHovered] = useState(false);
48
+ const login = useSignInWithChatGPT({
49
+ callbackPath,
50
+ clientId,
51
+ codeVerifier,
52
+ sessionStore,
53
+ extraParams,
54
+ fetch,
55
+ idTokenAddOrganizations,
56
+ issuer,
57
+ onSuccess,
58
+ onError,
59
+ onStateChange,
60
+ openMode,
61
+ redirectUri,
62
+ scope,
63
+ simplifiedFlow,
64
+ state,
65
+ });
66
+ const isBusy = login.status === "starting" || login.status === "redirecting";
67
+ const isSignedIn = login.status === "signed-in";
68
+ const handleClick = (event) => {
69
+ onClick?.(event);
70
+ if (event.defaultPrevented) {
71
+ return;
72
+ }
73
+ if (isSignedIn) {
74
+ void login.logout();
75
+ return;
76
+ }
77
+ void login.login();
78
+ };
79
+ const handleMouseEnter = (event) => {
80
+ setIsHovered(true);
81
+ onMouseEnter?.(event);
82
+ };
83
+ const handleMouseLeave = (event) => {
84
+ setIsHovered(false);
85
+ onMouseLeave?.(event);
86
+ };
87
+ if (hideWhenSignedIn && isSignedIn) {
88
+ return null;
89
+ }
90
+ const button = (_jsxs("button", { ...props, type: type, disabled: disabled || isBusy, onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: {
91
+ ...buttonStyle,
92
+ background: isHovered && !disabled && !isBusy ? "#f9f9f9" : "#ffffff",
93
+ ...style,
94
+ }, children: [showLogo ? _jsx(OpenAILogo, {}) : null, _jsx("span", { children: login.status === "redirecting"
95
+ ? redirectingLabel
96
+ : isBusy
97
+ ? loadingLabel
98
+ : isSignedIn
99
+ ? signedInLabel
100
+ : children })] }));
101
+ if (hideAttribution) {
102
+ return button;
103
+ }
104
+ return (_jsxs("span", { style: containerStyle, children: [button, _jsx("a", { href: "https://github.com/EvanZhouDev/openai-oauth", rel: "noreferrer", style: attributionStyle, target: "_blank", children: "Powered by OpenAI OAuth" })] }));
105
+ };
@@ -0,0 +1,5 @@
1
+ export { type BrowserSessionOptions, type BrowserSessionStoreOptions, createSessionStore, getSession, type OpenAIAuthHeadersOptions, openaiAuthHeaders, } from "@openai-oauth/web";
2
+ export { SignInWithChatGPT, type SignInWithChatGPTProps, } from "./SignInWithChatGPT.js";
3
+ export type { OpenAIOAuthSession, SessionStore, SignInWithChatGPTError, SignInWithChatGPTState, } from "./types.js";
4
+ export { type SignInWithChatGPTOpenMode, type UseSignInWithChatGPTOptions, type UseSignInWithChatGPTReturn, useSignInWithChatGPT, } from "./useSignInWithChatGPT.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,kBAAkB,EAClB,UAAU,EACV,KAAK,wBAAwB,EAC7B,iBAAiB,GACjB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACN,iBAAiB,EACjB,KAAK,sBAAsB,GAC3B,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EACX,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,GACtB,MAAM,YAAY,CAAA;AACnB,OAAO,EACN,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,EAC/B,oBAAoB,GACpB,MAAM,2BAA2B,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createSessionStore, getSession, openaiAuthHeaders, } from "@openai-oauth/web";
2
+ export { SignInWithChatGPT, } from "./SignInWithChatGPT.js";
3
+ export { useSignInWithChatGPT, } from "./useSignInWithChatGPT.js";
package/dist/next.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export declare const GET: (request: Request) => Promise<Response>;
2
+ export declare const POST: (request: Request) => Promise<Response>;
3
+ export declare const OPTIONS: (request: Request) => Promise<Response>;
4
+ //# sourceMappingURL=next.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,GAAG,GAAI,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAqB,CAAA;AAC5E,eAAO,MAAM,IAAI,GAAI,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAqB,CAAA;AAC7E,eAAO,MAAM,OAAO,GAAI,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAqB,CAAA"}
package/dist/next.js ADDED
@@ -0,0 +1,5 @@
1
+ import { createRelayHandler } from "@openai-oauth/web";
2
+ const handler = createRelayHandler();
3
+ export const GET = (request) => handler(request);
4
+ export const POST = (request) => handler(request);
5
+ export const OPTIONS = (request) => handler(request);
@@ -0,0 +1,2 @@
1
+ export { openaiCredentials, type WebServerOpenAIOAuthOptions, } from "@openai-oauth/web/server";
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EACjB,KAAK,2BAA2B,GAChC,MAAM,0BAA0B,CAAA"}
package/dist/server.js ADDED
@@ -0,0 +1 @@
1
+ export { openaiCredentials, } from "@openai-oauth/web/server";
@@ -0,0 +1,33 @@
1
+ import type { OpenAIOAuthSession, SessionStore } from "@openai-oauth/core";
2
+ export type { OpenAIOAuthSession, SessionStore };
3
+ export type SignInWithChatGPTError = {
4
+ code: "invalid-callback" | "popup-blocked" | "request-failed" | "not-authenticated";
5
+ message: string;
6
+ cause?: unknown;
7
+ };
8
+ export type SignInWithChatGPTState = {
9
+ status: "checking";
10
+ session: null;
11
+ error: null;
12
+ } | {
13
+ status: "signed-out";
14
+ session: null;
15
+ error: null;
16
+ } | {
17
+ status: "starting";
18
+ session: null;
19
+ error: null;
20
+ } | {
21
+ status: "redirecting";
22
+ session: null;
23
+ error: null;
24
+ } | {
25
+ status: "signed-in";
26
+ session: OpenAIOAuthSession;
27
+ error: null;
28
+ } | {
29
+ status: "error";
30
+ session: null;
31
+ error: SignInWithChatGPTError;
32
+ };
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAE1E,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,CAAA;AAEhD,MAAM,MAAM,sBAAsB,GAAG;IACpC,IAAI,EACD,kBAAkB,GAClB,eAAe,GACf,gBAAgB,GAChB,mBAAmB,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,sBAAsB,GAC/B;IACA,MAAM,EAAE,UAAU,CAAA;IAClB,OAAO,EAAE,IAAI,CAAA;IACb,KAAK,EAAE,IAAI,CAAA;CACV,GACD;IACA,MAAM,EAAE,YAAY,CAAA;IACpB,OAAO,EAAE,IAAI,CAAA;IACb,KAAK,EAAE,IAAI,CAAA;CACV,GACD;IACA,MAAM,EAAE,UAAU,CAAA;IAClB,OAAO,EAAE,IAAI,CAAA;IACb,KAAK,EAAE,IAAI,CAAA;CACV,GACD;IACA,MAAM,EAAE,aAAa,CAAA;IACrB,OAAO,EAAE,IAAI,CAAA;IACb,KAAK,EAAE,IAAI,CAAA;CACV,GACD;IACA,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,kBAAkB,CAAA;IAC3B,KAAK,EAAE,IAAI,CAAA;CACV,GACD;IACA,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,IAAI,CAAA;IACb,KAAK,EAAE,sBAAsB,CAAA;CAC5B,CAAA"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { type CompleteLoginOptions, type StartLoginOptions } from "@openai-oauth/web";
2
+ import type { OpenAIOAuthSession, SessionStore, SignInWithChatGPTError, SignInWithChatGPTState } from "./types.js";
3
+ export type SignInWithChatGPTOpenMode = "redirect" | "popup";
4
+ export type UseSignInWithChatGPTOptions = Omit<StartLoginOptions, "returnTo"> & Pick<CompleteLoginOptions, "fetch" | "now" | "tokenUrl"> & {
5
+ sessionStore?: SessionStore;
6
+ onStateChange?: (state: SignInWithChatGPTState) => void;
7
+ onSuccess?: (session: OpenAIOAuthSession) => void;
8
+ onError?: (error: SignInWithChatGPTError) => void;
9
+ };
10
+ export type UseSignInWithChatGPTReturn = SignInWithChatGPTState & {
11
+ isSignedIn: boolean;
12
+ login: () => Promise<void>;
13
+ logout: () => Promise<void>;
14
+ refresh: () => Promise<OpenAIOAuthSession | null>;
15
+ reset: () => void;
16
+ };
17
+ export declare const useSignInWithChatGPT: (options?: UseSignInWithChatGPTOptions) => UseSignInWithChatGPTReturn;
18
+ //# sourceMappingURL=useSignInWithChatGPT.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSignInWithChatGPT.d.ts","sourceRoot":"","sources":["../src/useSignInWithChatGPT.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,oBAAoB,EAKzB,KAAK,iBAAiB,EAEtB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,KAAK,EACX,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,sBAAsB,EACtB,MAAM,YAAY,CAAA;AAEnB,MAAM,MAAM,yBAAyB,GAAG,UAAU,GAAG,OAAO,CAAA;AAE5D,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,GAC5E,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,KAAK,GAAG,UAAU,CAAC,GAAG;IAC1D,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;IACvD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAA;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;CACjD,CAAA;AAEF,MAAM,MAAM,0BAA0B,GAAG,sBAAsB,GAAG;IACjE,UAAU,EAAE,OAAO,CAAA;IACnB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,OAAO,EAAE,MAAM,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;IACjD,KAAK,EAAE,MAAM,IAAI,CAAA;CACjB,CAAA;AA4CD,eAAO,MAAM,oBAAoB,GAChC,UAAS,2BAAgC,KACvC,0BA2QF,CAAA"}
@@ -0,0 +1,244 @@
1
+ import { logout as clearLogin, completeLogin, createSessionStore, refreshSession, startLogin, } from "@openai-oauth/web";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ const popupMessageType = "openai-oauth:signed-in";
4
+ const checkingState = {
5
+ status: "checking",
6
+ session: null,
7
+ error: null,
8
+ };
9
+ const signedOutState = {
10
+ status: "signed-out",
11
+ session: null,
12
+ error: null,
13
+ };
14
+ const isBrowser = () => typeof window !== "undefined";
15
+ const toLoginError = (error, code = "request-failed") => ({
16
+ code,
17
+ message: error instanceof Error ? error.message : "Sign in with ChatGPT failed.",
18
+ cause: error,
19
+ });
20
+ const notifyOpener = () => {
21
+ if (window.opener && window.opener !== window) {
22
+ window.opener.postMessage({ type: popupMessageType }, window.location.origin);
23
+ window.setTimeout(() => window.close(), 50);
24
+ }
25
+ };
26
+ const useLatest = (value) => {
27
+ const ref = useRef(value);
28
+ ref.current = value;
29
+ return ref;
30
+ };
31
+ export const useSignInWithChatGPT = (options = {}) => {
32
+ const { callbackPath = "/auth/callback", clientId, codeVerifier, sessionStore: providedSessionStore, extraParams, fetch: fetchImpl, idTokenAddOrganizations, issuer, now, onSuccess, onError, onStateChange, openMode = "redirect", redirectUri, scope, simplifiedFlow, state: configuredState, tokenUrl, } = options;
33
+ const onSuccessRef = useLatest(onSuccess);
34
+ const onErrorRef = useLatest(onError);
35
+ const onStateChangeRef = useLatest(onStateChange);
36
+ const defaultStore = useMemo(() => createSessionStore(), []);
37
+ const sessionStore = providedSessionStore ?? defaultStore;
38
+ const [state, setState] = useState(checkingState);
39
+ const signedInState = useCallback((session) => ({
40
+ status: "signed-in",
41
+ session,
42
+ error: null,
43
+ }), []);
44
+ const setLoginState = useCallback((next) => {
45
+ setState(next);
46
+ onStateChangeRef.current?.(next);
47
+ }, [onStateChangeRef]);
48
+ const fail = useCallback((error, code) => {
49
+ const loginError = toLoginError(error, code);
50
+ setLoginState({
51
+ status: "error",
52
+ session: null,
53
+ error: loginError,
54
+ });
55
+ onErrorRef.current?.(loginError);
56
+ }, [onErrorRef, setLoginState]);
57
+ const loadStoredSession = useCallback(async () => {
58
+ const session = await sessionStore.get();
59
+ if (!session) {
60
+ setLoginState(signedOutState);
61
+ return;
62
+ }
63
+ const next = signedInState(session);
64
+ setLoginState(next);
65
+ onSuccessRef.current?.(session);
66
+ }, [sessionStore, onSuccessRef, setLoginState, signedInState]);
67
+ const completeCallback = useCallback(async () => {
68
+ if (!isBrowser()) {
69
+ return false;
70
+ }
71
+ const session = await completeLogin({
72
+ clientId,
73
+ fetch: fetchImpl,
74
+ issuer,
75
+ now,
76
+ sessionStore,
77
+ tokenUrl,
78
+ });
79
+ if (!session) {
80
+ return false;
81
+ }
82
+ const next = signedInState(session);
83
+ setLoginState(next);
84
+ onSuccessRef.current?.(session);
85
+ notifyOpener();
86
+ return true;
87
+ }, [
88
+ clientId,
89
+ fetchImpl,
90
+ issuer,
91
+ now,
92
+ sessionStore,
93
+ tokenUrl,
94
+ onSuccessRef,
95
+ setLoginState,
96
+ signedInState,
97
+ ]);
98
+ useEffect(() => {
99
+ if (!isBrowser()) {
100
+ setLoginState(signedOutState);
101
+ return;
102
+ }
103
+ let current = true;
104
+ void (async () => {
105
+ try {
106
+ const completed = await completeCallback();
107
+ if (!current || completed) {
108
+ return;
109
+ }
110
+ await loadStoredSession();
111
+ }
112
+ catch (error) {
113
+ if (current) {
114
+ fail(error, "invalid-callback");
115
+ }
116
+ }
117
+ })();
118
+ return () => {
119
+ current = false;
120
+ };
121
+ }, [completeCallback, fail, loadStoredSession, setLoginState]);
122
+ useEffect(() => {
123
+ if (!isBrowser()) {
124
+ return;
125
+ }
126
+ const onMessage = (event) => {
127
+ if (event.origin === window.location.origin &&
128
+ typeof event.data === "object" &&
129
+ event.data !== null &&
130
+ "type" in event.data &&
131
+ event.data.type === popupMessageType) {
132
+ void loadStoredSession();
133
+ }
134
+ };
135
+ window.addEventListener("message", onMessage);
136
+ return () => window.removeEventListener("message", onMessage);
137
+ }, [loadStoredSession]);
138
+ const login = useCallback(async () => {
139
+ if (!isBrowser()) {
140
+ fail(new Error("Sign in with ChatGPT can only start in a browser."));
141
+ return;
142
+ }
143
+ try {
144
+ setLoginState({
145
+ status: "starting",
146
+ session: null,
147
+ error: null,
148
+ });
149
+ await startLogin({
150
+ callbackPath,
151
+ clientId,
152
+ codeVerifier,
153
+ extraParams,
154
+ idTokenAddOrganizations,
155
+ issuer,
156
+ openMode,
157
+ redirectUri,
158
+ scope,
159
+ simplifiedFlow,
160
+ state: configuredState,
161
+ });
162
+ setLoginState({
163
+ status: "redirecting",
164
+ session: null,
165
+ error: null,
166
+ });
167
+ }
168
+ catch (error) {
169
+ fail(error, error instanceof Error &&
170
+ error.message === "The ChatGPT login popup was blocked."
171
+ ? "popup-blocked"
172
+ : undefined);
173
+ }
174
+ }, [
175
+ callbackPath,
176
+ clientId,
177
+ codeVerifier,
178
+ configuredState,
179
+ extraParams,
180
+ fail,
181
+ idTokenAddOrganizations,
182
+ issuer,
183
+ openMode,
184
+ redirectUri,
185
+ scope,
186
+ setLoginState,
187
+ simplifiedFlow,
188
+ ]);
189
+ const logout = useCallback(async () => {
190
+ await clearLogin({ sessionStore });
191
+ setLoginState(signedOutState);
192
+ }, [sessionStore, setLoginState]);
193
+ const refresh = useCallback(async () => {
194
+ try {
195
+ const session = state.session ?? (await sessionStore.get());
196
+ if (!session?.refreshToken) {
197
+ fail(new Error("No refresh token is available."), "not-authenticated");
198
+ return null;
199
+ }
200
+ const refreshed = await refreshSession({
201
+ refreshToken: session.refreshToken,
202
+ }, {
203
+ clientId,
204
+ fetch: fetchImpl,
205
+ issuer,
206
+ now,
207
+ tokenUrl,
208
+ });
209
+ await sessionStore.set(refreshed);
210
+ const next = signedInState(refreshed);
211
+ setLoginState(next);
212
+ onSuccessRef.current?.(refreshed);
213
+ return refreshed;
214
+ }
215
+ catch (error) {
216
+ fail(error);
217
+ return null;
218
+ }
219
+ }, [
220
+ clientId,
221
+ fail,
222
+ fetchImpl,
223
+ issuer,
224
+ now,
225
+ onSuccessRef,
226
+ sessionStore,
227
+ setLoginState,
228
+ signedInState,
229
+ state.session,
230
+ tokenUrl,
231
+ ]);
232
+ const reset = useCallback(() => {
233
+ void clearLogin({ sessionStore });
234
+ setLoginState(signedOutState);
235
+ }, [sessionStore, setLoginState]);
236
+ return {
237
+ ...state,
238
+ isSignedIn: state.status === "signed-in",
239
+ login,
240
+ logout,
241
+ refresh,
242
+ reset,
243
+ };
244
+ };