@authgear/nextjs 0.3.0 → 0.4.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 CHANGED
@@ -218,10 +218,17 @@ export async function callMyApiAction() {
218
218
  | Export | Description |
219
219
  |---|---|
220
220
  | `<AuthgearProvider>` | React context provider, must wrap the app |
221
- | `useAuthgear()` | Returns `{ state, user, isLoaded, isAuthenticated, signIn, signOut }` |
221
+ | `useAuthgear()` | Returns `{ state, user, isLoaded, isAuthenticated, signIn, signOut, openPage }` |
222
222
  | `useUser()` | Returns `UserInfo \| null` |
223
223
  | `<SignInButton>` | Button that calls `signIn()` on click |
224
224
  | `<SignOutButton>` | Button that calls `signOut()` on click |
225
+ | `<UserSettingsButton>` | Button that opens Authgear account settings in a new tab |
226
+
227
+ **`AuthgearProvider` props**:
228
+
229
+ | Prop | Default | Description |
230
+ |---|---|---|
231
+ | `openPagePath` | `"/api/auth/open"` | Route used by `openPage()` and `<UserSettingsButton>` to pre-authenticate and redirect to an Authgear page |
225
232
 
226
233
  ### `SignInOptions`
227
234
 
@@ -26,4 +26,4 @@ export {
26
26
  DEFAULT_SCOPES,
27
27
  SessionState
28
28
  };
29
- //# sourceMappingURL=chunk-CCQOLKXU.js.map
29
+ //# sourceMappingURL=chunk-AJJAXXPI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["export interface AuthgearConfig {\n /** Authgear endpoint, e.g. \"https://myapp.authgear.cloud\" */\n endpoint: string;\n /** OAuth client ID */\n clientID: string;\n /** Redirect URI for OAuth callback, e.g. \"http://localhost:3000/api/auth/callback\" */\n redirectURI: string;\n /** Where to redirect after logout */\n postLogoutRedirectURI?: string;\n /** OAuth scopes. Defaults to [\"openid\", \"offline_access\", \"https://authgear.com/scopes/full-userinfo\"] */\n scopes?: string[];\n /** Secret key for encrypting session cookie (min 32 chars) */\n sessionSecret: string;\n /** Session cookie name. Defaults to \"authgear.session\" */\n cookieName?: string;\n /**\n * Whether to enable SSO (Single Sign-On) with other apps on the same Authgear tenant.\n * When `true` (default), Authgear silently reuses its server-side session if the user\n * is already logged in, so users are not prompted for credentials again.\n * Set to `false` to always show the login form (`prompt=login`), which is recommended\n * for single-app deployments where silent sign-in feels unexpected to the user.\n * Defaults to `true`.\n */\n isSSOEnabled?: boolean;\n}\n\n/**\n * Pages that can be opened in a new tab with the current user pre-authenticated.\n * Used by `getOpenURL` from `@authgear/nextjs/server` and by `openPage` / `<UserSettingsButton>`\n * from `@authgear/nextjs/client`.\n */\nexport enum Page {\n Settings = \"/settings\",\n}\n\n/**\n * OIDC `prompt` parameter values.\n * Pass to `signIn({ prompt })` or `SignInButton signInOptions={{ prompt }}` to control\n * whether Authgear shows the login form for a specific authentication call.\n *\n * @see https://docs.authgear.com/authentication-and-access/single-sign-on/force-authgear-to-show-login-page\n */\nexport enum PromptOption {\n /** Always show the login form, even if the user has an active Authgear session. */\n Login = \"login\",\n /** Never show the login form; return an error if the user is not already authenticated. */\n None = \"none\",\n}\n\nexport const DEFAULT_SCOPES = [\n \"openid\",\n \"offline_access\",\n \"https://authgear.com/scopes/full-userinfo\",\n];\n\nexport enum SessionState {\n Unknown = \"UNKNOWN\",\n NoSession = \"NO_SESSION\",\n Authenticated = \"AUTHENTICATED\",\n}\n\nexport interface SessionData {\n accessToken: string;\n refreshToken: string | null;\n idToken: string | null;\n expiresAt: number;\n}\n\nexport interface Session {\n state: SessionState;\n accessToken: string | null;\n refreshToken: string | null;\n idToken: string | null;\n expiresAt: number | null;\n user: UserInfo | null;\n}\n\nexport interface UserInfo {\n sub: string;\n email?: string;\n emailVerified?: boolean;\n phoneNumber?: string;\n phoneNumberVerified?: boolean;\n preferredUsername?: string;\n givenName?: string;\n familyName?: string;\n name?: string;\n picture?: string;\n roles?: string[];\n isAnonymous?: boolean;\n isVerified?: boolean;\n canReauthenticate?: boolean;\n customAttributes?: Record<string, unknown>;\n raw: Record<string, unknown>;\n}\n\nexport interface JWTPayload {\n sub: string;\n iss: string;\n aud: string | string[];\n exp: number;\n iat: number;\n jti?: string;\n client_id?: string;\n \"https://authgear.com/claims/user/is_anonymous\"?: boolean;\n \"https://authgear.com/claims/user/is_verified\"?: boolean;\n \"https://authgear.com/claims/user/can_reauthenticate\"?: boolean;\n \"https://authgear.com/claims/user/roles\"?: string[];\n [key: string]: unknown;\n}\n\nexport interface TokenResponse {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token?: string;\n id_token?: string;\n}\n\nexport interface AppSessionTokenResponse {\n app_session_token: string;\n expire_at: string;\n}\n\nexport interface OIDCConfiguration {\n authorization_endpoint: string;\n token_endpoint: string;\n userinfo_endpoint: string;\n revocation_endpoint: string;\n end_session_endpoint: string;\n jwks_uri: string;\n issuer: string;\n}\n"],"mappings":";AA+BO,IAAK,OAAL,kBAAKA,UAAL;AACL,EAAAA,MAAA,cAAW;AADD,SAAAA;AAAA,GAAA;AAWL,IAAK,eAAL,kBAAKC,kBAAL;AAEL,EAAAA,cAAA,WAAQ;AAER,EAAAA,cAAA,UAAO;AAJG,SAAAA;AAAA,GAAA;AAOL,IAAM,iBAAiB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAK,eAAL,kBAAKC,kBAAL;AACL,EAAAA,cAAA,aAAU;AACV,EAAAA,cAAA,eAAY;AACZ,EAAAA,cAAA,mBAAgB;AAHN,SAAAA;AAAA,GAAA;","names":["Page","PromptOption","SessionState"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DEFAULT_SCOPES
3
- } from "./chunk-CCQOLKXU.js";
3
+ } from "./chunk-AJJAXXPI.js";
4
4
 
5
5
  // src/config.ts
6
6
  function resolveConfig(config) {
@@ -232,4 +232,4 @@ export {
232
232
  deriveSessionState,
233
233
  isTokenExpired
234
234
  };
235
- //# sourceMappingURL=chunk-UERNRN5J.js.map
235
+ //# sourceMappingURL=chunk-LPGVCNZ6.js.map
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ButtonHTMLAttributes } from 'react';
3
- import { c as SessionState, U as UserInfo } from './types-Cx-gnfE7.js';
4
- export { a as PromptOption, S as Session } from './types-Cx-gnfE7.js';
3
+ import { c as SessionState, U as UserInfo } from './types-D6m4Hact.js';
4
+ export { P as Page, a as PromptOption, S as Session } from './types-D6m4Hact.js';
5
5
 
6
6
  interface SignInOptions {
7
7
  returnTo?: string;
@@ -21,8 +21,10 @@ interface AuthgearProviderProps {
21
21
  loginPath?: string;
22
22
  /** Path to the logout route. Defaults to "/api/auth/logout". */
23
23
  logoutPath?: string;
24
+ /** Path to the open-page route handler. Defaults to "/api/auth/open". */
25
+ openPagePath?: string;
24
26
  }
25
- declare function AuthgearProvider({ children, userInfoPath, loginPath, logoutPath, }: AuthgearProviderProps): react_jsx_runtime.JSX.Element;
27
+ declare function AuthgearProvider({ children, userInfoPath, loginPath, logoutPath, openPagePath, }: AuthgearProviderProps): react_jsx_runtime.JSX.Element;
26
28
 
27
29
  interface SignInButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
28
30
  signInOptions?: SignInOptions;
@@ -32,6 +34,15 @@ declare function SignInButton({ signInOptions, children, ...props }: SignInButto
32
34
  type SignOutButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
33
35
  declare function SignOutButton({ children, ...props }: SignOutButtonProps): react_jsx_runtime.JSX.Element;
34
36
 
37
+ type UserSettingsButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
38
+ /**
39
+ * A button that opens Authgear's account settings page in a new tab
40
+ * for the currently authenticated user. Requires `AuthgearProvider` as an ancestor.
41
+ *
42
+ * Uses `GET /api/auth/open?page=/settings` under the hood — no Server Action needed.
43
+ */
44
+ declare function UserSettingsButton({ children, ...props }: UserSettingsButtonProps): react_jsx_runtime.JSX.Element;
45
+
35
46
  interface UseAuthgearReturn {
36
47
  /** Current session state */
37
48
  state: SessionState;
@@ -45,10 +56,12 @@ interface UseAuthgearReturn {
45
56
  signIn: (options?: SignInOptions) => void;
46
57
  /** Navigate to the sign-out endpoint */
47
58
  signOut: () => void;
59
+ /** Open an Authgear page (e.g. Page.Settings) in a new tab */
60
+ openPage: (path: string) => void;
48
61
  }
49
62
  declare function useAuthgear(): UseAuthgearReturn;
50
63
 
51
64
  /** Returns the current user info, or null if not authenticated. */
52
65
  declare function useUser(): UserInfo | null;
53
66
 
54
- export { AuthgearProvider, type AuthgearProviderProps, SessionState, SignInButton, type SignInButtonProps, type SignInOptions, SignOutButton, type SignOutButtonProps, type UseAuthgearReturn, UserInfo, useAuthgear, useUser };
67
+ export { AuthgearProvider, type AuthgearProviderProps, SessionState, SignInButton, type SignInButtonProps, type SignInOptions, SignOutButton, type SignOutButtonProps, type UseAuthgearReturn, UserInfo, UserSettingsButton, type UserSettingsButtonProps, useAuthgear, useUser };
package/dist/client.js CHANGED
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
  import {
3
+ Page,
3
4
  PromptOption,
4
5
  SessionState
5
- } from "./chunk-CCQOLKXU.js";
6
+ } from "./chunk-AJJAXXPI.js";
6
7
 
7
8
  // src/components/AuthgearProvider.tsx
8
9
  import {
@@ -18,7 +19,8 @@ function AuthgearProvider({
18
19
  children,
19
20
  userInfoPath = "/api/auth/userinfo",
20
21
  loginPath = "/api/auth/login",
21
- logoutPath = "/api/auth/logout"
22
+ logoutPath = "/api/auth/logout",
23
+ openPagePath = "/api/auth/open"
22
24
  }) {
23
25
  const [state, setState] = useState("UNKNOWN" /* Unknown */);
24
26
  const [user, setUser] = useState(null);
@@ -70,7 +72,15 @@ function AuthgearProvider({
70
72
  const signOut = useCallback(() => {
71
73
  window.location.href = logoutPath;
72
74
  }, [logoutPath]);
73
- return /* @__PURE__ */ jsx(AuthgearContext.Provider, { value: { state, user, isLoaded, signIn, signOut }, children });
75
+ const openPage = useCallback(
76
+ (path) => {
77
+ const url = new URL(openPagePath, window.location.origin);
78
+ url.searchParams.set("page", path);
79
+ window.open(url.toString(), "_blank", "noopener,noreferrer");
80
+ },
81
+ [openPagePath]
82
+ );
83
+ return /* @__PURE__ */ jsx(AuthgearContext.Provider, { value: { state, user, isLoaded, signIn, signOut, openPage }, children });
74
84
  }
75
85
  function useAuthgearContext() {
76
86
  const ctx = useContext(AuthgearContext);
@@ -84,26 +94,46 @@ function useAuthgearContext() {
84
94
  import { jsx as jsx2 } from "react/jsx-runtime";
85
95
  function SignInButton({ signInOptions, children = "Sign In", ...props }) {
86
96
  const { signIn } = useAuthgearContext();
87
- return /* @__PURE__ */ jsx2("button", { ...props, onClick: () => signIn(signInOptions), children });
97
+ return /* @__PURE__ */ jsx2("button", { ...props, onClick: (e) => {
98
+ props.onClick?.(e);
99
+ signIn(signInOptions);
100
+ }, children });
88
101
  }
89
102
 
90
103
  // src/components/SignOutButton.tsx
91
104
  import { jsx as jsx3 } from "react/jsx-runtime";
92
105
  function SignOutButton({ children = "Sign Out", ...props }) {
93
106
  const { signOut } = useAuthgearContext();
94
- return /* @__PURE__ */ jsx3("button", { ...props, onClick: signOut, children });
107
+ return /* @__PURE__ */ jsx3("button", { ...props, onClick: (e) => {
108
+ props.onClick?.(e);
109
+ signOut();
110
+ }, children });
111
+ }
112
+
113
+ // src/components/UserSettingsButton.tsx
114
+ import { jsx as jsx4 } from "react/jsx-runtime";
115
+ function UserSettingsButton({
116
+ children = "Account Settings",
117
+ ...props
118
+ }) {
119
+ const { openPage } = useAuthgearContext();
120
+ return /* @__PURE__ */ jsx4("button", { ...props, onClick: (e) => {
121
+ props.onClick?.(e);
122
+ openPage("/settings" /* Settings */);
123
+ }, children });
95
124
  }
96
125
 
97
126
  // src/hooks/useAuthgear.ts
98
127
  function useAuthgear() {
99
- const { state, user, isLoaded, signIn, signOut } = useAuthgearContext();
128
+ const { state, user, isLoaded, signIn, signOut, openPage } = useAuthgearContext();
100
129
  return {
101
130
  state,
102
131
  user,
103
132
  isLoaded,
104
133
  isAuthenticated: state === "AUTHENTICATED" /* Authenticated */,
105
134
  signIn,
106
- signOut
135
+ signOut,
136
+ openPage
107
137
  };
108
138
  }
109
139
 
@@ -114,10 +144,12 @@ function useUser() {
114
144
  }
115
145
  export {
116
146
  AuthgearProvider,
147
+ Page,
117
148
  PromptOption,
118
149
  SessionState,
119
150
  SignInButton,
120
151
  SignOutButton,
152
+ UserSettingsButton,
121
153
  useAuthgear,
122
154
  useUser
123
155
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/AuthgearProvider.tsx","../src/components/SignInButton.tsx","../src/components/SignOutButton.tsx","../src/hooks/useAuthgear.ts","../src/hooks/useUser.ts"],"sourcesContent":["\"use client\";\n\nimport React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n type ReactNode,\n} from \"react\";\nimport { SessionState, type UserInfo } from \"../types.js\";\n\nexport interface AuthgearContextValue {\n state: SessionState;\n user: UserInfo | null;\n isLoaded: boolean;\n signIn: (options?: SignInOptions) => void;\n signOut: () => void;\n}\n\nexport interface SignInOptions {\n returnTo?: string;\n loginPath?: string;\n /**\n * OIDC `prompt` parameter for this sign-in call.\n * Overrides the global `isSSOEnabled` setting for this navigation.\n * Use `PromptOption.Login` or `PromptOption.None` for type-safe values.\n */\n prompt?: string;\n}\n\nconst AuthgearContext = createContext<AuthgearContextValue | null>(null);\n\nexport interface AuthgearProviderProps {\n children: ReactNode;\n /** Path to the userinfo API route. Defaults to \"/api/auth/userinfo\". */\n userInfoPath?: string;\n /** Path to the login route. Defaults to \"/api/auth/login\". */\n loginPath?: string;\n /** Path to the logout route. Defaults to \"/api/auth/logout\". */\n logoutPath?: string;\n}\n\nexport function AuthgearProvider({\n children,\n userInfoPath = \"/api/auth/userinfo\",\n loginPath = \"/api/auth/login\",\n logoutPath = \"/api/auth/logout\",\n}: AuthgearProviderProps) {\n const [state, setState] = useState<SessionState>(SessionState.Unknown);\n const [user, setUser] = useState<UserInfo | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchSession() {\n try {\n const res = await fetch(userInfoPath);\n if (cancelled) return;\n\n if (res.ok) {\n const userInfo = (await res.json()) as UserInfo;\n setState(SessionState.Authenticated);\n setUser(userInfo);\n } else {\n setState(SessionState.NoSession);\n setUser(null);\n }\n } catch {\n if (!cancelled) {\n setState(SessionState.NoSession);\n setUser(null);\n }\n } finally {\n if (!cancelled) {\n setIsLoaded(true);\n }\n }\n }\n\n fetchSession();\n return () => { cancelled = true; };\n }, [userInfoPath]);\n\n const signIn = useCallback(\n (options?: SignInOptions) => {\n const path = options?.loginPath ?? loginPath;\n const url = new URL(path, window.location.origin);\n if (options?.returnTo) {\n url.searchParams.set(\"returnTo\", options.returnTo);\n }\n if (options?.prompt != null) {\n url.searchParams.set(\"prompt\", options.prompt);\n }\n window.location.href = url.toString();\n },\n [loginPath],\n );\n\n const signOut = useCallback(() => {\n window.location.href = logoutPath;\n }, [logoutPath]);\n\n return (\n <AuthgearContext.Provider value={{ state, user, isLoaded, signIn, signOut }}>\n {children}\n </AuthgearContext.Provider>\n );\n}\n\nexport function useAuthgearContext(): AuthgearContextValue {\n const ctx = useContext(AuthgearContext);\n if (!ctx) {\n throw new Error(\"useAuthgearContext must be used within <AuthgearProvider>\");\n }\n return ctx;\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext, type SignInOptions } from \"./AuthgearProvider.js\";\n\nexport interface SignInButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n signInOptions?: SignInOptions;\n}\n\nexport function SignInButton({ signInOptions, children = \"Sign In\", ...props }: SignInButtonProps) {\n const { signIn } = useAuthgearContext();\n return (\n <button {...props} onClick={() => signIn(signInOptions)}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext } from \"./AuthgearProvider.js\";\n\nexport type SignOutButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;\n\nexport function SignOutButton({ children = \"Sign Out\", ...props }: SignOutButtonProps) {\n const { signOut } = useAuthgearContext();\n return (\n <button {...props} onClick={signOut}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport { useAuthgearContext, type SignInOptions } from \"../components/AuthgearProvider.js\";\nimport { SessionState, type UserInfo } from \"../types.js\";\n\nexport interface UseAuthgearReturn {\n /** Current session state */\n state: SessionState;\n /** Current user info, null if not authenticated */\n user: UserInfo | null;\n /** Whether the initial session check has completed */\n isLoaded: boolean;\n /** Whether the user is currently authenticated */\n isAuthenticated: boolean;\n /** Navigate to the sign-in page */\n signIn: (options?: SignInOptions) => void;\n /** Navigate to the sign-out endpoint */\n signOut: () => void;\n}\n\nexport function useAuthgear(): UseAuthgearReturn {\n const { state, user, isLoaded, signIn, signOut } = useAuthgearContext();\n\n return {\n state,\n user,\n isLoaded,\n isAuthenticated: state === SessionState.Authenticated,\n signIn,\n signOut,\n };\n}\n","\"use client\";\n\nimport { useAuthgearContext } from \"../components/AuthgearProvider.js\";\nimport type { UserInfo } from \"../types.js\";\n\n/** Returns the current user info, or null if not authenticated. */\nexport function useUser(): UserInfo | null {\n const { user } = useAuthgearContext();\n return user;\n}\n"],"mappings":";;;;;;;AAEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAgGH;AA1EJ,IAAM,kBAAkB,cAA2C,IAAI;AAYhE,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,aAAa;AACf,GAA0B;AACxB,QAAM,CAAC,OAAO,QAAQ,IAAI,gCAA2C;AACrE,QAAM,CAAC,MAAM,OAAO,IAAI,SAA0B,IAAI;AACtD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAE9C,YAAU,MAAM;AACd,QAAI,YAAY;AAEhB,mBAAe,eAAe;AAC5B,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,YAAY;AACpC,YAAI,UAAW;AAEf,YAAI,IAAI,IAAI;AACV,gBAAM,WAAY,MAAM,IAAI,KAAK;AACjC,sDAAmC;AACnC,kBAAQ,QAAQ;AAAA,QAClB,OAAO;AACL,+CAA+B;AAC/B,kBAAQ,IAAI;AAAA,QACd;AAAA,MACF,QAAQ;AACN,YAAI,CAAC,WAAW;AACd,+CAA+B;AAC/B,kBAAQ,IAAI;AAAA,QACd;AAAA,MACF,UAAE;AACA,YAAI,CAAC,WAAW;AACd,sBAAY,IAAI;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAEA,iBAAa;AACb,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAM;AAAA,EACnC,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,SAAS;AAAA,IACb,CAAC,YAA4B;AAC3B,YAAM,OAAO,SAAS,aAAa;AACnC,YAAM,MAAM,IAAI,IAAI,MAAM,OAAO,SAAS,MAAM;AAChD,UAAI,SAAS,UAAU;AACrB,YAAI,aAAa,IAAI,YAAY,QAAQ,QAAQ;AAAA,MACnD;AACA,UAAI,SAAS,UAAU,MAAM;AAC3B,YAAI,aAAa,IAAI,UAAU,QAAQ,MAAM;AAAA,MAC/C;AACA,aAAO,SAAS,OAAO,IAAI,SAAS;AAAA,IACtC;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,UAAU,YAAY,MAAM;AAChC,WAAO,SAAS,OAAO;AAAA,EACzB,GAAG,CAAC,UAAU,CAAC;AAEf,SACE,oBAAC,gBAAgB,UAAhB,EAAyB,OAAO,EAAE,OAAO,MAAM,UAAU,QAAQ,QAAQ,GACvE,UACH;AAEJ;AAEO,SAAS,qBAA2C;AACzD,QAAM,MAAM,WAAW,eAAe;AACtC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,2DAA2D;AAAA,EAC7E;AACA,SAAO;AACT;;;ACzGI,gBAAAA,YAAA;AAHG,SAAS,aAAa,EAAE,eAAe,WAAW,WAAW,GAAG,MAAM,GAAsB;AACjG,QAAM,EAAE,OAAO,IAAI,mBAAmB;AACtC,SACE,gBAAAA,KAAC,YAAQ,GAAG,OAAO,SAAS,MAAM,OAAO,aAAa,GACnD,UACH;AAEJ;;;ACNI,gBAAAC,YAAA;AAHG,SAAS,cAAc,EAAE,WAAW,YAAY,GAAG,MAAM,GAAuB;AACrF,QAAM,EAAE,QAAQ,IAAI,mBAAmB;AACvC,SACE,gBAAAA,KAAC,YAAQ,GAAG,OAAO,SAAS,SACzB,UACH;AAEJ;;;ACMO,SAAS,cAAiC;AAC/C,QAAM,EAAE,OAAO,MAAM,UAAU,QAAQ,QAAQ,IAAI,mBAAmB;AAEtE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AACF;;;ACzBO,SAAS,UAA2B;AACzC,QAAM,EAAE,KAAK,IAAI,mBAAmB;AACpC,SAAO;AACT;","names":["jsx","jsx"]}
1
+ {"version":3,"sources":["../src/components/AuthgearProvider.tsx","../src/components/SignInButton.tsx","../src/components/SignOutButton.tsx","../src/components/UserSettingsButton.tsx","../src/hooks/useAuthgear.ts","../src/hooks/useUser.ts"],"sourcesContent":["\"use client\";\n\nimport React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n type ReactNode,\n} from \"react\";\nimport { SessionState, type UserInfo } from \"../types.js\";\n\nexport interface AuthgearContextValue {\n state: SessionState;\n user: UserInfo | null;\n isLoaded: boolean;\n signIn: (options?: SignInOptions) => void;\n signOut: () => void;\n /** Open an Authgear page (e.g. Page.Settings) in a new tab for the current user */\n openPage: (path: string) => void;\n}\n\nexport interface SignInOptions {\n returnTo?: string;\n loginPath?: string;\n /**\n * OIDC `prompt` parameter for this sign-in call.\n * Overrides the global `isSSOEnabled` setting for this navigation.\n * Use `PromptOption.Login` or `PromptOption.None` for type-safe values.\n */\n prompt?: string;\n}\n\nconst AuthgearContext = createContext<AuthgearContextValue | null>(null);\n\nexport interface AuthgearProviderProps {\n children: ReactNode;\n /** Path to the userinfo API route. Defaults to \"/api/auth/userinfo\". */\n userInfoPath?: string;\n /** Path to the login route. Defaults to \"/api/auth/login\". */\n loginPath?: string;\n /** Path to the logout route. Defaults to \"/api/auth/logout\". */\n logoutPath?: string;\n /** Path to the open-page route handler. Defaults to \"/api/auth/open\". */\n openPagePath?: string;\n}\n\nexport function AuthgearProvider({\n children,\n userInfoPath = \"/api/auth/userinfo\",\n loginPath = \"/api/auth/login\",\n logoutPath = \"/api/auth/logout\",\n openPagePath = \"/api/auth/open\",\n}: AuthgearProviderProps) {\n const [state, setState] = useState<SessionState>(SessionState.Unknown);\n const [user, setUser] = useState<UserInfo | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchSession() {\n try {\n const res = await fetch(userInfoPath);\n if (cancelled) return;\n\n if (res.ok) {\n const userInfo = (await res.json()) as UserInfo;\n setState(SessionState.Authenticated);\n setUser(userInfo);\n } else {\n setState(SessionState.NoSession);\n setUser(null);\n }\n } catch {\n if (!cancelled) {\n setState(SessionState.NoSession);\n setUser(null);\n }\n } finally {\n if (!cancelled) {\n setIsLoaded(true);\n }\n }\n }\n\n fetchSession();\n return () => { cancelled = true; };\n }, [userInfoPath]);\n\n const signIn = useCallback(\n (options?: SignInOptions) => {\n const path = options?.loginPath ?? loginPath;\n const url = new URL(path, window.location.origin);\n if (options?.returnTo) {\n url.searchParams.set(\"returnTo\", options.returnTo);\n }\n if (options?.prompt != null) {\n url.searchParams.set(\"prompt\", options.prompt);\n }\n window.location.href = url.toString();\n },\n [loginPath],\n );\n\n const signOut = useCallback(() => {\n window.location.href = logoutPath;\n }, [logoutPath]);\n\n const openPage = useCallback(\n (path: string) => {\n const url = new URL(openPagePath, window.location.origin);\n url.searchParams.set(\"page\", path);\n window.open(url.toString(), \"_blank\", \"noopener,noreferrer\");\n },\n [openPagePath],\n );\n\n return (\n <AuthgearContext.Provider value={{ state, user, isLoaded, signIn, signOut, openPage }}>\n {children}\n </AuthgearContext.Provider>\n );\n}\n\nexport function useAuthgearContext(): AuthgearContextValue {\n const ctx = useContext(AuthgearContext);\n if (!ctx) {\n throw new Error(\"useAuthgearContext must be used within <AuthgearProvider>\");\n }\n return ctx;\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext, type SignInOptions } from \"./AuthgearProvider.js\";\n\nexport interface SignInButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n signInOptions?: SignInOptions;\n}\n\nexport function SignInButton({ signInOptions, children = \"Sign In\", ...props }: SignInButtonProps) {\n const { signIn } = useAuthgearContext();\n return (\n <button {...props} onClick={(e) => { props.onClick?.(e); signIn(signInOptions); }}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext } from \"./AuthgearProvider.js\";\n\nexport type SignOutButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;\n\nexport function SignOutButton({ children = \"Sign Out\", ...props }: SignOutButtonProps) {\n const { signOut } = useAuthgearContext();\n return (\n <button {...props} onClick={(e) => { props.onClick?.(e); signOut(); }}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport React, { type ButtonHTMLAttributes } from \"react\";\nimport { useAuthgearContext } from \"./AuthgearProvider.js\";\nimport { Page } from \"../types.js\";\n\nexport type UserSettingsButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * A button that opens Authgear's account settings page in a new tab\n * for the currently authenticated user. Requires `AuthgearProvider` as an ancestor.\n *\n * Uses `GET /api/auth/open?page=/settings` under the hood — no Server Action needed.\n */\nexport function UserSettingsButton({\n children = \"Account Settings\",\n ...props\n}: UserSettingsButtonProps) {\n const { openPage } = useAuthgearContext();\n return (\n <button {...props} onClick={(e) => { props.onClick?.(e); openPage(Page.Settings); }}>\n {children}\n </button>\n );\n}\n","\"use client\";\n\nimport { useAuthgearContext, type SignInOptions } from \"../components/AuthgearProvider.js\";\nimport { SessionState, type UserInfo } from \"../types.js\";\n\nexport interface UseAuthgearReturn {\n /** Current session state */\n state: SessionState;\n /** Current user info, null if not authenticated */\n user: UserInfo | null;\n /** Whether the initial session check has completed */\n isLoaded: boolean;\n /** Whether the user is currently authenticated */\n isAuthenticated: boolean;\n /** Navigate to the sign-in page */\n signIn: (options?: SignInOptions) => void;\n /** Navigate to the sign-out endpoint */\n signOut: () => void;\n /** Open an Authgear page (e.g. Page.Settings) in a new tab */\n openPage: (path: string) => void;\n}\n\nexport function useAuthgear(): UseAuthgearReturn {\n const { state, user, isLoaded, signIn, signOut, openPage } = useAuthgearContext();\n\n return {\n state,\n user,\n isLoaded,\n isAuthenticated: state === SessionState.Authenticated,\n signIn,\n signOut,\n openPage,\n };\n}\n","\"use client\";\n\nimport { useAuthgearContext } from \"../components/AuthgearProvider.js\";\nimport type { UserInfo } from \"../types.js\";\n\n/** Returns the current user info, or null if not authenticated. */\nexport function useUser(): UserInfo | null {\n const { user } = useAuthgearContext();\n return user;\n}\n"],"mappings":"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","names":["jsx","jsx","jsx"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
- import { A as AuthgearConfig } from './types-Cx-gnfE7.js';
3
- export { D as DEFAULT_SCOPES, J as JWTPayload, O as OIDCConfiguration, P as Page, a as PromptOption, S as Session, b as SessionData, c as SessionState, T as TokenResponse, U as UserInfo } from './types-Cx-gnfE7.js';
2
+ import { A as AuthgearConfig } from './types-D6m4Hact.js';
3
+ export { D as DEFAULT_SCOPES, J as JWTPayload, O as OIDCConfiguration, P as Page, a as PromptOption, S as Session, b as SessionData, c as SessionState, T as TokenResponse, U as UserInfo } from './types-D6m4Hact.js';
4
4
 
5
5
  /**
6
6
  * Creates Next.js route handlers for all Authgear auth endpoints.
@@ -17,6 +17,7 @@ export { D as DEFAULT_SCOPES, J as JWTPayload, O as OIDCConfiguration, P as Page
17
17
  * - GET /api/auth/logout — Logout and revoke tokens
18
18
  * - POST /api/auth/refresh — Refresh access token
19
19
  * - GET /api/auth/userinfo — Get current user info
20
+ * - GET /api/auth/open — Open an Authgear page (e.g. /settings) for the current user
20
21
  */
21
22
  declare function createAuthgearHandlers(config: AuthgearConfig): {
22
23
  GET: (request: NextRequest, { params }: {
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  buildAuthorizeURL,
3
+ buildOpenURL,
3
4
  generateCodeVerifier,
4
5
  generateState,
5
6
  parseUserInfo
@@ -12,20 +13,21 @@ import {
12
13
  decryptSession,
13
14
  exchangeCode,
14
15
  fetchOIDCConfiguration,
16
+ getAppSessionToken,
15
17
  isTokenExpired,
16
18
  refreshAccessToken,
17
19
  resolveConfig,
18
20
  revokeToken
19
- } from "./chunk-UERNRN5J.js";
21
+ } from "./chunk-LPGVCNZ6.js";
20
22
  import {
21
23
  DEFAULT_SCOPES,
22
24
  Page,
23
25
  PromptOption,
24
26
  SessionState
25
- } from "./chunk-CCQOLKXU.js";
27
+ } from "./chunk-AJJAXXPI.js";
26
28
 
27
29
  // src/handlers/index.ts
28
- import { NextResponse as NextResponse6 } from "next/server";
30
+ import { NextResponse as NextResponse7 } from "next/server";
29
31
 
30
32
  // src/handlers/login.ts
31
33
  import { NextResponse } from "next/server";
@@ -232,6 +234,46 @@ async function handleUserInfo(request, config) {
232
234
  return response;
233
235
  }
234
236
 
237
+ // src/handlers/open.ts
238
+ import { NextResponse as NextResponse6 } from "next/server";
239
+ var ALLOWED_PAGES = new Set(Object.values(Page));
240
+ async function handleOpen(request, config) {
241
+ const resolved = resolveConfig(config);
242
+ const pageParam = request.nextUrl.searchParams.get("page");
243
+ if (!pageParam || !ALLOWED_PAGES.has(pageParam)) {
244
+ return NextResponse6.json({ error: "invalid_page" }, { status: 400 });
245
+ }
246
+ const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;
247
+ if (!sessionCookieValue) {
248
+ return NextResponse6.json({ error: "not_authenticated" }, { status: 401 });
249
+ }
250
+ const sessionData = decryptSession(sessionCookieValue, resolved.sessionSecret);
251
+ if (!sessionData) {
252
+ return NextResponse6.json({ error: "not_authenticated" }, { status: 401 });
253
+ }
254
+ if (!sessionData.refreshToken) {
255
+ return NextResponse6.json({ error: "no_refresh_token" }, { status: 401 });
256
+ }
257
+ const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);
258
+ let app_session_token;
259
+ try {
260
+ const tokenResponse = await getAppSessionToken(
261
+ resolved.endpoint,
262
+ sessionData.refreshToken
263
+ );
264
+ app_session_token = tokenResponse.app_session_token;
265
+ } catch {
266
+ return NextResponse6.json({ error: "not_authenticated" }, { status: 401 });
267
+ }
268
+ const url = buildOpenURL(oidcConfig, {
269
+ clientID: resolved.clientID,
270
+ appSessionToken: app_session_token,
271
+ targetPath: pageParam,
272
+ scopes: resolved.scopes
273
+ });
274
+ return NextResponse6.redirect(url, 302);
275
+ }
276
+
235
277
  // src/handlers/index.ts
236
278
  function createAuthgearHandlers(config) {
237
279
  async function GET(request, { params }) {
@@ -246,8 +288,10 @@ function createAuthgearHandlers(config) {
246
288
  return handleLogout(request, config);
247
289
  case "userinfo":
248
290
  return handleUserInfo(request, config);
291
+ case "open":
292
+ return handleOpen(request, config);
249
293
  default:
250
- return NextResponse6.json({ error: "not_found" }, { status: 404 });
294
+ return NextResponse7.json({ error: "not_found" }, { status: 404 });
251
295
  }
252
296
  }
253
297
  async function POST(request, { params }) {
@@ -257,7 +301,7 @@ function createAuthgearHandlers(config) {
257
301
  case "refresh":
258
302
  return handleRefresh(request, config);
259
303
  default:
260
- return NextResponse6.json({ error: "not_found" }, { status: 404 });
304
+ return NextResponse7.json({ error: "not_found" }, { status: 404 });
261
305
  }
262
306
  }
263
307
  return { GET, POST };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/handlers/index.ts","../src/handlers/login.ts","../src/handlers/callback.ts","../src/handlers/logout.ts","../src/handlers/refresh.ts","../src/handlers/userinfo.ts"],"sourcesContent":["import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { handleLogin } from \"./login.js\";\nimport { handleCallback } from \"./callback.js\";\nimport { handleLogout } from \"./logout.js\";\nimport { handleRefresh } from \"./refresh.js\";\nimport { handleUserInfo } from \"./userinfo.js\";\n\n/**\n * Creates Next.js route handlers for all Authgear auth endpoints.\n *\n * Usage in `app/api/auth/[...authgear]/route.ts`:\n * ```ts\n * import { createAuthgearHandlers } from \"@authgear/nextjs\";\n * export const { GET, POST } = createAuthgearHandlers(config);\n * ```\n *\n * Routes handled:\n * - GET /api/auth/login — Start OAuth flow\n * - GET /api/auth/callback — Handle OAuth callback\n * - GET /api/auth/logout — Logout and revoke tokens\n * - POST /api/auth/refresh — Refresh access token\n * - GET /api/auth/userinfo — Get current user info\n */\nexport function createAuthgearHandlers(config: AuthgearConfig) {\n async function GET(\n request: NextRequest,\n { params }: { params: Promise<{ authgear: string[] }> },\n ): Promise<NextResponse> {\n const { authgear } = await params;\n const action = authgear?.[0];\n\n switch (action) {\n case \"login\":\n return handleLogin(request, config);\n case \"callback\":\n return handleCallback(request, config);\n case \"logout\":\n return handleLogout(request, config);\n case \"userinfo\":\n return handleUserInfo(request, config);\n default:\n return NextResponse.json({ error: \"not_found\" }, { status: 404 });\n }\n }\n\n async function POST(\n request: NextRequest,\n { params }: { params: Promise<{ authgear: string[] }> },\n ): Promise<NextResponse> {\n const { authgear } = await params;\n const action = authgear?.[0];\n\n switch (action) {\n case \"refresh\":\n return handleRefresh(request, config);\n default:\n return NextResponse.json({ error: \"not_found\" }, { status: 404 });\n }\n }\n\n return { GET, POST };\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { PromptOption } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { generateCodeVerifier } from \"../oauth/pkce.js\";\nimport { buildAuthorizeURL, generateState } from \"../oauth/authorize.js\";\nimport { buildPKCECookie } from \"../session/cookie.js\";\n\nexport async function handleLogin(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n const returnTo = request.nextUrl.searchParams.get(\"returnTo\") ?? \"/\";\n const codeVerifier = generateCodeVerifier();\n const state = generateState();\n\n // Per-call ?prompt= query param takes precedence over the global isSSOEnabled default\n const ALLOWED_PROMPTS = new Set<string>(Object.values(PromptOption));\n const rawPrompt = request.nextUrl.searchParams.get(\"prompt\");\n const perCallPrompt = rawPrompt !== null && ALLOWED_PROMPTS.has(rawPrompt) ? rawPrompt : undefined;\n const prompt = perCallPrompt ?? (resolved.isSSOEnabled ? undefined : \"login\");\n\n const authorizeURL = buildAuthorizeURL(oidcConfig, {\n clientID: resolved.clientID,\n redirectURI: resolved.redirectURI,\n scopes: resolved.scopes,\n codeVerifier,\n state,\n prompt,\n });\n\n const pkceCookie = buildPKCECookie({ codeVerifier, state, returnTo }, resolved.sessionSecret);\n\n const response = NextResponse.redirect(authorizeURL);\n response.cookies.set(pkceCookie.name, pkceCookie.value, {\n httpOnly: pkceCookie.httpOnly,\n secure: pkceCookie.secure,\n sameSite: pkceCookie.sameSite,\n path: pkceCookie.path,\n maxAge: pkceCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { exchangeCode } from \"../oauth/token.js\";\nimport { decryptPKCECookie, buildSessionCookie } from \"../session/cookie.js\";\n\nexport async function handleCallback(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const code = request.nextUrl.searchParams.get(\"code\");\n const state = request.nextUrl.searchParams.get(\"state\");\n const error = request.nextUrl.searchParams.get(\"error\");\n const errorDescription = request.nextUrl.searchParams.get(\"error_description\");\n\n if (error) {\n return NextResponse.json(\n { error, error_description: errorDescription },\n { status: 400 },\n );\n }\n\n if (!code || !state) {\n return NextResponse.json(\n { error: \"missing_params\", error_description: \"Missing code or state parameter\" },\n { status: 400 },\n );\n }\n\n // Validate PKCE state\n const pkceCookieValue = request.cookies.get(\"authgear.pkce\")?.value;\n if (!pkceCookieValue) {\n return NextResponse.json(\n { error: \"invalid_state\", error_description: \"Missing PKCE cookie\" },\n { status: 400 },\n );\n }\n\n const pkceData = decryptPKCECookie(pkceCookieValue, resolved.sessionSecret);\n if (!pkceData || pkceData.state !== state) {\n return NextResponse.json(\n { error: \"invalid_state\", error_description: \"State mismatch\" },\n { status: 400 },\n );\n }\n\n // Exchange code for tokens\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await exchangeCode(oidcConfig, {\n code,\n codeVerifier: pkceData.codeVerifier,\n clientID: resolved.clientID,\n redirectURI: resolved.redirectURI,\n });\n\n // Build session cookie\n const sessionCookie = buildSessionCookie(\n resolved.cookieName,\n {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? null,\n idToken: tokenResponse.id_token ?? null,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n },\n resolved.sessionSecret,\n );\n\n const returnTo = pkceData.returnTo || \"/\";\n const redirectURL = new URL(returnTo, request.nextUrl.origin);\n const response = NextResponse.redirect(redirectURL);\n\n // Set session cookie\n response.cookies.set(sessionCookie.name, sessionCookie.value, {\n httpOnly: sessionCookie.httpOnly,\n secure: sessionCookie.secure,\n sameSite: sessionCookie.sameSite,\n path: sessionCookie.path,\n maxAge: sessionCookie.maxAge,\n });\n\n // Clear PKCE cookie\n response.cookies.set(\"authgear.pkce\", \"\", { maxAge: 0, path: \"/\" });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { revokeToken } from \"../oauth/token.js\";\nimport { decryptSession, buildClearCookie } from \"../session/cookie.js\";\n\nexport async function handleLogout(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n // Revoke refresh token if present\n const sessionCookie = request.cookies.get(resolved.cookieName)?.value;\n if (sessionCookie) {\n const session = decryptSession(sessionCookie, resolved.sessionSecret);\n if (session?.refreshToken) {\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n try {\n await revokeToken(oidcConfig, session.refreshToken);\n } catch {\n // Best-effort revocation\n }\n }\n }\n\n const clearCookie = buildClearCookie(resolved.cookieName);\n const redirectURL = new URL(resolved.postLogoutRedirectURI, request.nextUrl.origin);\n const response = NextResponse.redirect(redirectURL);\n\n response.cookies.set(clearCookie.name, clearCookie.value, {\n httpOnly: clearCookie.httpOnly,\n secure: clearCookie.secure,\n sameSite: clearCookie.sameSite,\n path: clearCookie.path,\n maxAge: clearCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { refreshAccessToken } from \"../oauth/token.js\";\nimport { decryptSession, buildSessionCookie } from \"../session/cookie.js\";\n\nexport async function handleRefresh(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"no_session\" }, { status: 401 });\n }\n\n const session = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!session?.refreshToken) {\n return NextResponse.json({ error: \"no_refresh_token\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: session.refreshToken,\n clientID: resolved.clientID,\n });\n\n const newSession = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? session.refreshToken,\n idToken: tokenResponse.id_token ?? session.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n\n const sessionCookie = buildSessionCookie(resolved.cookieName, newSession, resolved.sessionSecret);\n const response = NextResponse.json({ ok: true });\n\n response.cookies.set(sessionCookie.name, sessionCookie.value, {\n httpOnly: sessionCookie.httpOnly,\n secure: sessionCookie.secure,\n sameSite: sessionCookie.sameSite,\n path: sessionCookie.path,\n maxAge: sessionCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig, UserInfo } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { decryptSession } from \"../session/cookie.js\";\nimport { isTokenExpired } from \"../session/state.js\";\nimport { refreshAccessToken } from \"../oauth/token.js\";\nimport { buildSessionCookie } from \"../session/cookie.js\";\nimport { parseUserInfo } from \"../user.js\";\n\nexport async function handleUserInfo(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"no_session\" }, { status: 401 });\n }\n\n let session = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!session) {\n return NextResponse.json({ error: \"invalid_session\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n // Auto-refresh if access token is expired\n if (isTokenExpired(session.expiresAt) && session.refreshToken) {\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: session.refreshToken,\n clientID: resolved.clientID,\n });\n session = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? session.refreshToken,\n idToken: tokenResponse.id_token ?? session.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n }\n\n // Fetch user info from Authgear\n const userinfoRes = await fetch(oidcConfig.userinfo_endpoint, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n });\n\n if (!userinfoRes.ok) {\n return NextResponse.json({ error: \"userinfo_failed\" }, { status: userinfoRes.status });\n }\n\n const raw = (await userinfoRes.json()) as Record<string, unknown>;\n const userInfo: UserInfo = parseUserInfo(raw);\n\n const response = NextResponse.json(userInfo);\n\n // Update session cookie if tokens were refreshed\n const newCookie = buildSessionCookie(resolved.cookieName, session, resolved.sessionSecret);\n response.cookies.set(newCookie.name, newCookie.value, {\n httpOnly: newCookie.httpOnly,\n secure: newCookie.secure,\n sameSite: newCookie.sameSite,\n path: newCookie.path,\n maxAge: newCookie.maxAge,\n });\n\n return response;\n}\n"],"mappings":"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","names":["NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse"]}
1
+ {"version":3,"sources":["../src/handlers/index.ts","../src/handlers/login.ts","../src/handlers/callback.ts","../src/handlers/logout.ts","../src/handlers/refresh.ts","../src/handlers/userinfo.ts","../src/handlers/open.ts"],"sourcesContent":["import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { handleLogin } from \"./login.js\";\nimport { handleCallback } from \"./callback.js\";\nimport { handleLogout } from \"./logout.js\";\nimport { handleRefresh } from \"./refresh.js\";\nimport { handleUserInfo } from \"./userinfo.js\";\nimport { handleOpen } from \"./open.js\";\n\n/**\n * Creates Next.js route handlers for all Authgear auth endpoints.\n *\n * Usage in `app/api/auth/[...authgear]/route.ts`:\n * ```ts\n * import { createAuthgearHandlers } from \"@authgear/nextjs\";\n * export const { GET, POST } = createAuthgearHandlers(config);\n * ```\n *\n * Routes handled:\n * - GET /api/auth/login — Start OAuth flow\n * - GET /api/auth/callback — Handle OAuth callback\n * - GET /api/auth/logout — Logout and revoke tokens\n * - POST /api/auth/refresh — Refresh access token\n * - GET /api/auth/userinfo — Get current user info\n * - GET /api/auth/open — Open an Authgear page (e.g. /settings) for the current user\n */\nexport function createAuthgearHandlers(config: AuthgearConfig) {\n async function GET(\n request: NextRequest,\n { params }: { params: Promise<{ authgear: string[] }> },\n ): Promise<NextResponse> {\n const { authgear } = await params;\n const action = authgear?.[0];\n\n switch (action) {\n case \"login\":\n return handleLogin(request, config);\n case \"callback\":\n return handleCallback(request, config);\n case \"logout\":\n return handleLogout(request, config);\n case \"userinfo\":\n return handleUserInfo(request, config);\n case \"open\":\n return handleOpen(request, config);\n default:\n return NextResponse.json({ error: \"not_found\" }, { status: 404 });\n }\n }\n\n async function POST(\n request: NextRequest,\n { params }: { params: Promise<{ authgear: string[] }> },\n ): Promise<NextResponse> {\n const { authgear } = await params;\n const action = authgear?.[0];\n\n switch (action) {\n case \"refresh\":\n return handleRefresh(request, config);\n default:\n return NextResponse.json({ error: \"not_found\" }, { status: 404 });\n }\n }\n\n return { GET, POST };\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { PromptOption } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { generateCodeVerifier } from \"../oauth/pkce.js\";\nimport { buildAuthorizeURL, generateState } from \"../oauth/authorize.js\";\nimport { buildPKCECookie } from \"../session/cookie.js\";\n\nexport async function handleLogin(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n const returnTo = request.nextUrl.searchParams.get(\"returnTo\") ?? \"/\";\n const codeVerifier = generateCodeVerifier();\n const state = generateState();\n\n // Per-call ?prompt= query param takes precedence over the global isSSOEnabled default\n const ALLOWED_PROMPTS = new Set<string>(Object.values(PromptOption));\n const rawPrompt = request.nextUrl.searchParams.get(\"prompt\");\n const perCallPrompt = rawPrompt !== null && ALLOWED_PROMPTS.has(rawPrompt) ? rawPrompt : undefined;\n const prompt = perCallPrompt ?? (resolved.isSSOEnabled ? undefined : \"login\");\n\n const authorizeURL = buildAuthorizeURL(oidcConfig, {\n clientID: resolved.clientID,\n redirectURI: resolved.redirectURI,\n scopes: resolved.scopes,\n codeVerifier,\n state,\n prompt,\n });\n\n const pkceCookie = buildPKCECookie({ codeVerifier, state, returnTo }, resolved.sessionSecret);\n\n const response = NextResponse.redirect(authorizeURL);\n response.cookies.set(pkceCookie.name, pkceCookie.value, {\n httpOnly: pkceCookie.httpOnly,\n secure: pkceCookie.secure,\n sameSite: pkceCookie.sameSite,\n path: pkceCookie.path,\n maxAge: pkceCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { exchangeCode } from \"../oauth/token.js\";\nimport { decryptPKCECookie, buildSessionCookie } from \"../session/cookie.js\";\n\nexport async function handleCallback(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const code = request.nextUrl.searchParams.get(\"code\");\n const state = request.nextUrl.searchParams.get(\"state\");\n const error = request.nextUrl.searchParams.get(\"error\");\n const errorDescription = request.nextUrl.searchParams.get(\"error_description\");\n\n if (error) {\n return NextResponse.json(\n { error, error_description: errorDescription },\n { status: 400 },\n );\n }\n\n if (!code || !state) {\n return NextResponse.json(\n { error: \"missing_params\", error_description: \"Missing code or state parameter\" },\n { status: 400 },\n );\n }\n\n // Validate PKCE state\n const pkceCookieValue = request.cookies.get(\"authgear.pkce\")?.value;\n if (!pkceCookieValue) {\n return NextResponse.json(\n { error: \"invalid_state\", error_description: \"Missing PKCE cookie\" },\n { status: 400 },\n );\n }\n\n const pkceData = decryptPKCECookie(pkceCookieValue, resolved.sessionSecret);\n if (!pkceData || pkceData.state !== state) {\n return NextResponse.json(\n { error: \"invalid_state\", error_description: \"State mismatch\" },\n { status: 400 },\n );\n }\n\n // Exchange code for tokens\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await exchangeCode(oidcConfig, {\n code,\n codeVerifier: pkceData.codeVerifier,\n clientID: resolved.clientID,\n redirectURI: resolved.redirectURI,\n });\n\n // Build session cookie\n const sessionCookie = buildSessionCookie(\n resolved.cookieName,\n {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? null,\n idToken: tokenResponse.id_token ?? null,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n },\n resolved.sessionSecret,\n );\n\n const returnTo = pkceData.returnTo || \"/\";\n const redirectURL = new URL(returnTo, request.nextUrl.origin);\n const response = NextResponse.redirect(redirectURL);\n\n // Set session cookie\n response.cookies.set(sessionCookie.name, sessionCookie.value, {\n httpOnly: sessionCookie.httpOnly,\n secure: sessionCookie.secure,\n sameSite: sessionCookie.sameSite,\n path: sessionCookie.path,\n maxAge: sessionCookie.maxAge,\n });\n\n // Clear PKCE cookie\n response.cookies.set(\"authgear.pkce\", \"\", { maxAge: 0, path: \"/\" });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { revokeToken } from \"../oauth/token.js\";\nimport { decryptSession, buildClearCookie } from \"../session/cookie.js\";\n\nexport async function handleLogout(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n // Revoke refresh token if present\n const sessionCookie = request.cookies.get(resolved.cookieName)?.value;\n if (sessionCookie) {\n const session = decryptSession(sessionCookie, resolved.sessionSecret);\n if (session?.refreshToken) {\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n try {\n await revokeToken(oidcConfig, session.refreshToken);\n } catch {\n // Best-effort revocation\n }\n }\n }\n\n const clearCookie = buildClearCookie(resolved.cookieName);\n const redirectURL = new URL(resolved.postLogoutRedirectURI, request.nextUrl.origin);\n const response = NextResponse.redirect(redirectURL);\n\n response.cookies.set(clearCookie.name, clearCookie.value, {\n httpOnly: clearCookie.httpOnly,\n secure: clearCookie.secure,\n sameSite: clearCookie.sameSite,\n path: clearCookie.path,\n maxAge: clearCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { refreshAccessToken } from \"../oauth/token.js\";\nimport { decryptSession, buildSessionCookie } from \"../session/cookie.js\";\n\nexport async function handleRefresh(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"no_session\" }, { status: 401 });\n }\n\n const session = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!session?.refreshToken) {\n return NextResponse.json({ error: \"no_refresh_token\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: session.refreshToken,\n clientID: resolved.clientID,\n });\n\n const newSession = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? session.refreshToken,\n idToken: tokenResponse.id_token ?? session.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n\n const sessionCookie = buildSessionCookie(resolved.cookieName, newSession, resolved.sessionSecret);\n const response = NextResponse.json({ ok: true });\n\n response.cookies.set(sessionCookie.name, sessionCookie.value, {\n httpOnly: sessionCookie.httpOnly,\n secure: sessionCookie.secure,\n sameSite: sessionCookie.sameSite,\n path: sessionCookie.path,\n maxAge: sessionCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig, UserInfo } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { decryptSession } from \"../session/cookie.js\";\nimport { isTokenExpired } from \"../session/state.js\";\nimport { refreshAccessToken } from \"../oauth/token.js\";\nimport { buildSessionCookie } from \"../session/cookie.js\";\nimport { parseUserInfo } from \"../user.js\";\n\nexport async function handleUserInfo(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"no_session\" }, { status: 401 });\n }\n\n let session = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!session) {\n return NextResponse.json({ error: \"invalid_session\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n // Auto-refresh if access token is expired\n if (isTokenExpired(session.expiresAt) && session.refreshToken) {\n const tokenResponse = await refreshAccessToken(oidcConfig, {\n refreshToken: session.refreshToken,\n clientID: resolved.clientID,\n });\n session = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token ?? session.refreshToken,\n idToken: tokenResponse.id_token ?? session.idToken,\n expiresAt: Math.floor(Date.now() / 1000) + tokenResponse.expires_in,\n };\n }\n\n // Fetch user info from Authgear\n const userinfoRes = await fetch(oidcConfig.userinfo_endpoint, {\n headers: { Authorization: `Bearer ${session.accessToken}` },\n });\n\n if (!userinfoRes.ok) {\n return NextResponse.json({ error: \"userinfo_failed\" }, { status: userinfoRes.status });\n }\n\n const raw = (await userinfoRes.json()) as Record<string, unknown>;\n const userInfo: UserInfo = parseUserInfo(raw);\n\n const response = NextResponse.json(userInfo);\n\n // Update session cookie if tokens were refreshed\n const newCookie = buildSessionCookie(resolved.cookieName, session, resolved.sessionSecret);\n response.cookies.set(newCookie.name, newCookie.value, {\n httpOnly: newCookie.httpOnly,\n secure: newCookie.secure,\n sameSite: newCookie.sameSite,\n path: newCookie.path,\n maxAge: newCookie.maxAge,\n });\n\n return response;\n}\n","import { NextResponse, type NextRequest } from \"next/server\";\nimport type { AuthgearConfig } from \"../types.js\";\nimport { Page } from \"../types.js\";\nimport { resolveConfig } from \"../config.js\";\nimport { fetchOIDCConfiguration } from \"../oauth/discovery.js\";\nimport { getAppSessionToken } from \"../oauth/token.js\";\nimport { buildOpenURL } from \"../oauth/authorize.js\";\nimport { decryptSession } from \"../session/cookie.js\";\n\nconst ALLOWED_PAGES = new Set<string>(Object.values(Page));\n\nexport async function handleOpen(\n request: NextRequest,\n config: AuthgearConfig,\n): Promise<NextResponse> {\n const resolved = resolveConfig(config);\n\n const pageParam = request.nextUrl.searchParams.get(\"page\");\n if (!pageParam || !ALLOWED_PAGES.has(pageParam)) {\n return NextResponse.json({ error: \"invalid_page\" }, { status: 400 });\n }\n\n const sessionCookieValue = request.cookies.get(resolved.cookieName)?.value;\n if (!sessionCookieValue) {\n return NextResponse.json({ error: \"not_authenticated\" }, { status: 401 });\n }\n\n const sessionData = decryptSession(sessionCookieValue, resolved.sessionSecret);\n if (!sessionData) {\n return NextResponse.json({ error: \"not_authenticated\" }, { status: 401 });\n }\n\n if (!sessionData.refreshToken) {\n return NextResponse.json({ error: \"no_refresh_token\" }, { status: 401 });\n }\n\n const oidcConfig = await fetchOIDCConfiguration(resolved.endpoint);\n\n let app_session_token: string;\n try {\n const tokenResponse = await getAppSessionToken(\n resolved.endpoint,\n sessionData.refreshToken,\n );\n app_session_token = tokenResponse.app_session_token;\n } catch {\n return NextResponse.json({ error: \"not_authenticated\" }, { status: 401 });\n }\n\n const url = buildOpenURL(oidcConfig, {\n clientID: resolved.clientID,\n appSessionToken: app_session_token,\n targetPath: pageParam,\n scopes: resolved.scopes,\n });\n\n return NextResponse.redirect(url, 302);\n}\n"],"mappings":"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","names":["NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse","NextResponse"]}
package/dist/proxy.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
- import { A as AuthgearConfig } from './types-Cx-gnfE7.js';
2
+ import { A as AuthgearConfig } from './types-D6m4Hact.js';
3
3
 
4
4
  interface AuthgearProxyOptions extends AuthgearConfig {
5
5
  /**
package/dist/proxy.js CHANGED
@@ -5,8 +5,8 @@ import {
5
5
  isTokenExpired,
6
6
  refreshAccessToken,
7
7
  resolveConfig
8
- } from "./chunk-UERNRN5J.js";
9
- import "./chunk-CCQOLKXU.js";
8
+ } from "./chunk-LPGVCNZ6.js";
9
+ import "./chunk-AJJAXXPI.js";
10
10
 
11
11
  // src/proxy.ts
12
12
  import { NextResponse } from "next/server";
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AuthgearConfig, S as Session, U as UserInfo, P as Page, J as JWTPayload } from './types-Cx-gnfE7.js';
2
- export { a as PromptOption, c as SessionState } from './types-Cx-gnfE7.js';
1
+ import { A as AuthgearConfig, S as Session, U as UserInfo, P as Page, J as JWTPayload } from './types-D6m4Hact.js';
2
+ export { a as PromptOption, c as SessionState } from './types-D6m4Hact.js';
3
3
 
4
4
  /**
5
5
  * Read the current session in a Server Component, Route Handler, or Server Action.
package/dist/server.js CHANGED
@@ -11,12 +11,12 @@ import {
11
11
  isTokenExpired,
12
12
  refreshAccessToken,
13
13
  resolveConfig
14
- } from "./chunk-UERNRN5J.js";
14
+ } from "./chunk-LPGVCNZ6.js";
15
15
  import {
16
16
  Page,
17
17
  PromptOption,
18
18
  SessionState
19
- } from "./chunk-CCQOLKXU.js";
19
+ } from "./chunk-AJJAXXPI.js";
20
20
 
21
21
  // src/server.ts
22
22
  import "server-only";
@@ -24,7 +24,9 @@ interface AuthgearConfig {
24
24
  isSSOEnabled?: boolean;
25
25
  }
26
26
  /**
27
- * Pages that can be opened via `getOpenURL` from `@authgear/nextjs/server`.
27
+ * Pages that can be opened in a new tab with the current user pre-authenticated.
28
+ * Used by `getOpenURL` from `@authgear/nextjs/server` and by `openPage` / `<UserSettingsButton>`
29
+ * from `@authgear/nextjs/client`.
28
30
  */
29
31
  declare enum Page {
30
32
  Settings = "/settings"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authgear/nextjs",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Authgear SDK for Next.js 16 - OAuth authentication, session management, and JWT verification",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["export interface AuthgearConfig {\n /** Authgear endpoint, e.g. \"https://myapp.authgear.cloud\" */\n endpoint: string;\n /** OAuth client ID */\n clientID: string;\n /** Redirect URI for OAuth callback, e.g. \"http://localhost:3000/api/auth/callback\" */\n redirectURI: string;\n /** Where to redirect after logout */\n postLogoutRedirectURI?: string;\n /** OAuth scopes. Defaults to [\"openid\", \"offline_access\", \"https://authgear.com/scopes/full-userinfo\"] */\n scopes?: string[];\n /** Secret key for encrypting session cookie (min 32 chars) */\n sessionSecret: string;\n /** Session cookie name. Defaults to \"authgear.session\" */\n cookieName?: string;\n /**\n * Whether to enable SSO (Single Sign-On) with other apps on the same Authgear tenant.\n * When `true` (default), Authgear silently reuses its server-side session if the user\n * is already logged in, so users are not prompted for credentials again.\n * Set to `false` to always show the login form (`prompt=login`), which is recommended\n * for single-app deployments where silent sign-in feels unexpected to the user.\n * Defaults to `true`.\n */\n isSSOEnabled?: boolean;\n}\n\n/**\n * Pages that can be opened via `getOpenURL` from `@authgear/nextjs/server`.\n */\nexport enum Page {\n Settings = \"/settings\",\n}\n\n/**\n * OIDC `prompt` parameter values.\n * Pass to `signIn({ prompt })` or `SignInButton signInOptions={{ prompt }}` to control\n * whether Authgear shows the login form for a specific authentication call.\n *\n * @see https://docs.authgear.com/authentication-and-access/single-sign-on/force-authgear-to-show-login-page\n */\nexport enum PromptOption {\n /** Always show the login form, even if the user has an active Authgear session. */\n Login = \"login\",\n /** Never show the login form; return an error if the user is not already authenticated. */\n None = \"none\",\n}\n\nexport const DEFAULT_SCOPES = [\n \"openid\",\n \"offline_access\",\n \"https://authgear.com/scopes/full-userinfo\",\n];\n\nexport enum SessionState {\n Unknown = \"UNKNOWN\",\n NoSession = \"NO_SESSION\",\n Authenticated = \"AUTHENTICATED\",\n}\n\nexport interface SessionData {\n accessToken: string;\n refreshToken: string | null;\n idToken: string | null;\n expiresAt: number;\n}\n\nexport interface Session {\n state: SessionState;\n accessToken: string | null;\n refreshToken: string | null;\n idToken: string | null;\n expiresAt: number | null;\n user: UserInfo | null;\n}\n\nexport interface UserInfo {\n sub: string;\n email?: string;\n emailVerified?: boolean;\n phoneNumber?: string;\n phoneNumberVerified?: boolean;\n preferredUsername?: string;\n givenName?: string;\n familyName?: string;\n name?: string;\n picture?: string;\n roles?: string[];\n isAnonymous?: boolean;\n isVerified?: boolean;\n canReauthenticate?: boolean;\n customAttributes?: Record<string, unknown>;\n raw: Record<string, unknown>;\n}\n\nexport interface JWTPayload {\n sub: string;\n iss: string;\n aud: string | string[];\n exp: number;\n iat: number;\n jti?: string;\n client_id?: string;\n \"https://authgear.com/claims/user/is_anonymous\"?: boolean;\n \"https://authgear.com/claims/user/is_verified\"?: boolean;\n \"https://authgear.com/claims/user/can_reauthenticate\"?: boolean;\n \"https://authgear.com/claims/user/roles\"?: string[];\n [key: string]: unknown;\n}\n\nexport interface TokenResponse {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token?: string;\n id_token?: string;\n}\n\nexport interface AppSessionTokenResponse {\n app_session_token: string;\n expire_at: string;\n}\n\nexport interface OIDCConfiguration {\n authorization_endpoint: string;\n token_endpoint: string;\n userinfo_endpoint: string;\n revocation_endpoint: string;\n end_session_endpoint: string;\n jwks_uri: string;\n issuer: string;\n}\n"],"mappings":";AA6BO,IAAK,OAAL,kBAAKA,UAAL;AACL,EAAAA,MAAA,cAAW;AADD,SAAAA;AAAA,GAAA;AAWL,IAAK,eAAL,kBAAKC,kBAAL;AAEL,EAAAA,cAAA,WAAQ;AAER,EAAAA,cAAA,UAAO;AAJG,SAAAA;AAAA,GAAA;AAOL,IAAM,iBAAiB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAK,eAAL,kBAAKC,kBAAL;AACL,EAAAA,cAAA,aAAU;AACV,EAAAA,cAAA,eAAY;AACZ,EAAAA,cAAA,mBAAgB;AAHN,SAAAA;AAAA,GAAA;","names":["Page","PromptOption","SessionState"]}