@oauth42/next 0.1.0 → 0.2.1

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
@@ -12,6 +12,11 @@ yarn add @oauth42/next
12
12
  pnpm add @oauth42/next
13
13
  ```
14
14
 
15
+ ## Tutorials
16
+
17
+ - **[Getting Started with Hosted Auth](https://oauth42.com/docs/tutorials/hosted-auth-nextjs)** - Complete tutorial for integrating OAuth42's hosted authentication pages (recommended for new projects)
18
+ - Self-hosted authentication - Build your own login UI with full control
19
+
15
20
  ## Quick Start
16
21
 
17
22
  ### 1. Environment Variables
@@ -110,6 +115,8 @@ export default NextAuth({
110
115
 
111
116
  ### Middleware Protection
112
117
 
118
+ #### Option 1: Using SDK Helper (Simple)
119
+
113
120
  ```typescript
114
121
  // middleware.ts
115
122
  import { withOAuth42Auth } from '@oauth42/next/server';
@@ -128,6 +135,32 @@ export const config = {
128
135
  };
129
136
  ```
130
137
 
138
+ #### Option 2: With Custom Cookie Names
139
+
140
+ If you're using custom cookie names in your NextAuth configuration, use `withAuth` directly:
141
+
142
+ ```typescript
143
+ // middleware.ts
144
+ import { withAuth } from "next-auth/middleware"
145
+
146
+ export default withAuth({
147
+ pages: {
148
+ signIn: '/auth/signin',
149
+ },
150
+ cookies: {
151
+ sessionToken: {
152
+ name: 'your-custom-cookie-name', // Match your authOptions
153
+ },
154
+ },
155
+ })
156
+
157
+ export const config = {
158
+ matcher: ['/', '/protected/:path*'],
159
+ }
160
+ ```
161
+
162
+ **Note:** When using middleware, you don't need client-side auth checks in page components - the middleware handles authentication before the page renders.
163
+
131
164
  ### Server-Side Session
132
165
 
133
166
  ```typescript
@@ -108,4 +108,83 @@ interface ProtectedComponentProps {
108
108
  */
109
109
  declare function ProtectedComponent({ children, fallback, loadingComponent, }: ProtectedComponentProps): react_jsx_runtime.JSX.Element;
110
110
 
111
- export { AuthStatus, type AuthStatusProps, type OAuth42Session, ProtectedComponent, type ProtectedComponentProps, SignInButton, type SignInButtonProps, SignOutButton, type SignOutButtonProps, type UseOAuth42SessionReturn, UserProfile, type UserProfileProps, useOAuth42Session, useOAuth42Tokens, useOAuth42User, useRequireAuth };
111
+ /**
112
+ * Utilities for OAuth42 Hosted Authentication
113
+ */
114
+ interface HostedAuthOptions {
115
+ /** OAuth2 client ID */
116
+ clientId: string;
117
+ /** Redirect URI after authentication */
118
+ redirectUri: string;
119
+ /** OAuth2 scopes (space-separated) */
120
+ scope?: string;
121
+ /** OAuth2 state parameter for CSRF protection */
122
+ state?: string;
123
+ /** Base URL for hosted auth (defaults to production) */
124
+ baseUrl?: string;
125
+ }
126
+ /**
127
+ * Redirect to OAuth42 hosted authentication pages
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * import { redirectToHostedAuth } from '@oauth42/next/client';
132
+ *
133
+ * function LoginButton() {
134
+ * return (
135
+ * <button onClick={() => redirectToHostedAuth({
136
+ * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID!,
137
+ * redirectUri: `${window.location.origin}/api/auth/callback`,
138
+ * })}>
139
+ * Sign in with OAuth42
140
+ * </button>
141
+ * );
142
+ * }
143
+ * ```
144
+ */
145
+ declare function redirectToHostedAuth(options: HostedAuthOptions): void;
146
+ /**
147
+ * Verify state parameter on OAuth2 callback
148
+ * Call this in your callback page to verify the state matches
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * import { verifyState } from '@oauth42/next/client';
153
+ *
154
+ * export default function CallbackPage() {
155
+ * const searchParams = useSearchParams();
156
+ * const state = searchParams.get('state');
157
+ *
158
+ * if (!verifyState(state)) {
159
+ * return <div>Invalid state parameter</div>;
160
+ * }
161
+ *
162
+ * // Continue with token exchange...
163
+ * }
164
+ * ```
165
+ */
166
+ declare function verifyState(state: string | null): boolean;
167
+ /**
168
+ * Configuration for hosted authentication
169
+ */
170
+ interface HostedAuthConfig {
171
+ /** Enable hosted authentication */
172
+ enabled: boolean;
173
+ /** Base URL for hosted auth pages (optional, defaults to production) */
174
+ baseUrl?: string;
175
+ /** Feature flags for hosted auth */
176
+ features?: {
177
+ /** Allow user signup */
178
+ signup?: boolean;
179
+ /** Allow social login */
180
+ socialLogin?: boolean;
181
+ /** Allow password reset */
182
+ passwordReset?: boolean;
183
+ };
184
+ }
185
+ /**
186
+ * Default hosted auth configuration
187
+ */
188
+ declare const DEFAULT_HOSTED_AUTH_CONFIG: HostedAuthConfig;
189
+
190
+ export { AuthStatus, type AuthStatusProps, DEFAULT_HOSTED_AUTH_CONFIG, type HostedAuthConfig, type HostedAuthOptions, type OAuth42Session, ProtectedComponent, type ProtectedComponentProps, SignInButton, type SignInButtonProps, SignOutButton, type SignOutButtonProps, type UseOAuth42SessionReturn, UserProfile, type UserProfileProps, redirectToHostedAuth, useOAuth42Session, useOAuth42Tokens, useOAuth42User, useRequireAuth, verifyState };
@@ -108,4 +108,83 @@ interface ProtectedComponentProps {
108
108
  */
109
109
  declare function ProtectedComponent({ children, fallback, loadingComponent, }: ProtectedComponentProps): react_jsx_runtime.JSX.Element;
110
110
 
111
- export { AuthStatus, type AuthStatusProps, type OAuth42Session, ProtectedComponent, type ProtectedComponentProps, SignInButton, type SignInButtonProps, SignOutButton, type SignOutButtonProps, type UseOAuth42SessionReturn, UserProfile, type UserProfileProps, useOAuth42Session, useOAuth42Tokens, useOAuth42User, useRequireAuth };
111
+ /**
112
+ * Utilities for OAuth42 Hosted Authentication
113
+ */
114
+ interface HostedAuthOptions {
115
+ /** OAuth2 client ID */
116
+ clientId: string;
117
+ /** Redirect URI after authentication */
118
+ redirectUri: string;
119
+ /** OAuth2 scopes (space-separated) */
120
+ scope?: string;
121
+ /** OAuth2 state parameter for CSRF protection */
122
+ state?: string;
123
+ /** Base URL for hosted auth (defaults to production) */
124
+ baseUrl?: string;
125
+ }
126
+ /**
127
+ * Redirect to OAuth42 hosted authentication pages
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * import { redirectToHostedAuth } from '@oauth42/next/client';
132
+ *
133
+ * function LoginButton() {
134
+ * return (
135
+ * <button onClick={() => redirectToHostedAuth({
136
+ * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID!,
137
+ * redirectUri: `${window.location.origin}/api/auth/callback`,
138
+ * })}>
139
+ * Sign in with OAuth42
140
+ * </button>
141
+ * );
142
+ * }
143
+ * ```
144
+ */
145
+ declare function redirectToHostedAuth(options: HostedAuthOptions): void;
146
+ /**
147
+ * Verify state parameter on OAuth2 callback
148
+ * Call this in your callback page to verify the state matches
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * import { verifyState } from '@oauth42/next/client';
153
+ *
154
+ * export default function CallbackPage() {
155
+ * const searchParams = useSearchParams();
156
+ * const state = searchParams.get('state');
157
+ *
158
+ * if (!verifyState(state)) {
159
+ * return <div>Invalid state parameter</div>;
160
+ * }
161
+ *
162
+ * // Continue with token exchange...
163
+ * }
164
+ * ```
165
+ */
166
+ declare function verifyState(state: string | null): boolean;
167
+ /**
168
+ * Configuration for hosted authentication
169
+ */
170
+ interface HostedAuthConfig {
171
+ /** Enable hosted authentication */
172
+ enabled: boolean;
173
+ /** Base URL for hosted auth pages (optional, defaults to production) */
174
+ baseUrl?: string;
175
+ /** Feature flags for hosted auth */
176
+ features?: {
177
+ /** Allow user signup */
178
+ signup?: boolean;
179
+ /** Allow social login */
180
+ socialLogin?: boolean;
181
+ /** Allow password reset */
182
+ passwordReset?: boolean;
183
+ };
184
+ }
185
+ /**
186
+ * Default hosted auth configuration
187
+ */
188
+ declare const DEFAULT_HOSTED_AUTH_CONFIG: HostedAuthConfig;
189
+
190
+ export { AuthStatus, type AuthStatusProps, DEFAULT_HOSTED_AUTH_CONFIG, type HostedAuthConfig, type HostedAuthOptions, type OAuth42Session, ProtectedComponent, type ProtectedComponentProps, SignInButton, type SignInButtonProps, SignOutButton, type SignOutButtonProps, type UseOAuth42SessionReturn, UserProfile, type UserProfileProps, redirectToHostedAuth, useOAuth42Session, useOAuth42Tokens, useOAuth42User, useRequireAuth, verifyState };
@@ -21,18 +21,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var client_exports = {};
22
22
  __export(client_exports, {
23
23
  AuthStatus: () => AuthStatus,
24
+ DEFAULT_HOSTED_AUTH_CONFIG: () => DEFAULT_HOSTED_AUTH_CONFIG,
24
25
  ProtectedComponent: () => ProtectedComponent,
25
26
  SessionProvider: () => import_react4.SessionProvider,
26
27
  SignInButton: () => SignInButton,
27
28
  SignOutButton: () => SignOutButton,
28
29
  UserProfile: () => UserProfile,
30
+ redirectToHostedAuth: () => redirectToHostedAuth,
29
31
  signIn: () => import_react4.signIn,
30
32
  signOut: () => import_react4.signOut,
31
33
  useOAuth42Session: () => useOAuth42Session,
32
34
  useOAuth42Tokens: () => useOAuth42Tokens,
33
35
  useOAuth42User: () => useOAuth42User,
34
36
  useRequireAuth: () => useRequireAuth,
35
- useSession: () => import_react4.useSession
37
+ useSession: () => import_react4.useSession,
38
+ verifyState: () => verifyState
36
39
  });
37
40
  module.exports = __toCommonJS(client_exports);
38
41
  var import_react4 = require("next-auth/react");
@@ -215,20 +218,67 @@ function ProtectedComponent({
215
218
  }
216
219
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
217
220
  }
221
+
222
+ // src/utils/hosted-auth.ts
223
+ function generateState() {
224
+ const array = new Uint8Array(32);
225
+ crypto.getRandomValues(array);
226
+ return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
227
+ }
228
+ function redirectToHostedAuth(options) {
229
+ const {
230
+ clientId,
231
+ redirectUri,
232
+ scope = "openid profile email",
233
+ state = generateState(),
234
+ baseUrl = "https://hosted-auth.oauth42.com"
235
+ } = options;
236
+ const params = new URLSearchParams({
237
+ client_id: clientId,
238
+ redirect_uri: redirectUri,
239
+ response_type: "code",
240
+ scope,
241
+ state
242
+ });
243
+ const authUrl = `${baseUrl}/login?${params.toString()}`;
244
+ if (typeof window !== "undefined") {
245
+ sessionStorage.setItem("oauth42_state", state);
246
+ window.location.href = authUrl;
247
+ }
248
+ }
249
+ function verifyState(state) {
250
+ if (typeof window === "undefined") return false;
251
+ if (!state) return false;
252
+ const storedState = sessionStorage.getItem("oauth42_state");
253
+ sessionStorage.removeItem("oauth42_state");
254
+ return storedState === state;
255
+ }
256
+ var DEFAULT_HOSTED_AUTH_CONFIG = {
257
+ enabled: true,
258
+ baseUrl: "https://hosted-auth.oauth42.com",
259
+ features: {
260
+ signup: true,
261
+ socialLogin: false,
262
+ passwordReset: true
263
+ }
264
+ };
218
265
  // Annotate the CommonJS export names for ESM import in node:
219
266
  0 && (module.exports = {
220
267
  AuthStatus,
268
+ DEFAULT_HOSTED_AUTH_CONFIG,
221
269
  ProtectedComponent,
222
270
  SessionProvider,
223
271
  SignInButton,
224
272
  SignOutButton,
225
273
  UserProfile,
274
+ redirectToHostedAuth,
226
275
  signIn,
227
276
  signOut,
228
277
  useOAuth42Session,
229
278
  useOAuth42Tokens,
230
279
  useOAuth42User,
231
280
  useRequireAuth,
232
- useSession
281
+ useSession,
282
+ verifyState
233
283
  });
234
284
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts","../../src/client/hooks.ts","../../src/client/components.tsx"],"sourcesContent":["// Client-side exports\n\n// Re-export commonly used next-auth/react functions\nexport { signIn, signOut, useSession, SessionProvider } from 'next-auth/react';\nexport type { Session } from 'next-auth';\nexport {\n useOAuth42Session,\n useOAuth42User,\n useOAuth42Tokens,\n useRequireAuth,\n} from './hooks';\n\nexport type {\n OAuth42Session,\n UseOAuth42SessionReturn,\n} from './hooks';\n\nexport {\n SignInButton,\n SignOutButton,\n UserProfile,\n AuthStatus,\n ProtectedComponent,\n} from './components';\n\nexport type {\n SignInButtonProps,\n SignOutButtonProps,\n UserProfileProps,\n AuthStatusProps,\n ProtectedComponentProps,\n} from './components';","import { useSession, signIn, signOut } from 'next-auth/react';\nimport { useCallback, useEffect, useState } from 'react';\n\nexport type OAuth42Session<E = {}> = ({\n user?: {\n email?: string | null;\n name?: string | null;\n image?: string | null;\n username?: string;\n emailVerified?: boolean;\n };\n accessToken?: string;\n idToken?: string;\n expires?: string;\n}) & E;\n\nexport interface UseOAuth42SessionReturn<E = {}> {\n session: OAuth42Session<E> | null;\n loading: boolean;\n error: Error | null;\n isAuthenticated: boolean;\n signIn: () => Promise<void>;\n signOut: () => Promise<void>;\n}\n\n/**\n * Hook to manage OAuth42 session with optional extra fields\n */\nexport function useOAuth42Session<E = {}>(): UseOAuth42SessionReturn<E> {\n const { data: session, status } = useSession();\n const [error, setError] = useState<Error | null>(null);\n \n const handleSignIn = useCallback(async () => {\n try {\n setError(null);\n await signIn('oauth42');\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n const handleSignOut = useCallback(async () => {\n try {\n setError(null);\n await signOut();\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n return {\n session: session as unknown as OAuth42Session<E> | null,\n loading: status === 'loading',\n error,\n isAuthenticated: status === 'authenticated',\n signIn: handleSignIn,\n signOut: handleSignOut,\n };\n}\n\n/**\n * Hook to get the current OAuth42 user\n */\nexport function useOAuth42User<E = {}>() {\n const { session, isAuthenticated } = useOAuth42Session<E>();\n \n return {\n user: isAuthenticated ? session?.user : null,\n isAuthenticated,\n };\n}\n\n/**\n * Hook to manage OAuth42 tokens\n */\nexport function useOAuth42Tokens<E = {}>() {\n const { session } = useOAuth42Session<E>();\n const [isExpired, setIsExpired] = useState(false);\n \n useEffect(() => {\n if (session?.expires) {\n const expiryTime = new Date(session.expires).getTime();\n const now = Date.now();\n setIsExpired(now >= expiryTime);\n \n // Set a timer to update expiry status\n const timeUntilExpiry = expiryTime - now;\n if (timeUntilExpiry > 0) {\n const timer = setTimeout(() => {\n setIsExpired(true);\n }, timeUntilExpiry);\n \n return () => clearTimeout(timer);\n }\n }\n }, [session?.expires]);\n \n return {\n accessToken: session?.accessToken,\n idToken: session?.idToken,\n isExpired,\n refreshToken: async () => {\n // Trigger a session refresh\n await signIn('oauth42');\n },\n };\n}\n\n/**\n * Hook for protected routes\n */\nexport function useRequireAuth(redirectTo: string = '/auth/signin') {\n const { isAuthenticated, loading } = useOAuth42Session();\n const [isRedirecting, setIsRedirecting] = useState(false);\n \n useEffect(() => {\n if (!loading && !isAuthenticated && !isRedirecting) {\n setIsRedirecting(true);\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [isAuthenticated, loading, redirectTo, isRedirecting]);\n \n return {\n isAuthenticated,\n loading: loading || isRedirecting,\n };\n}\n","import React from 'react';\nimport { signIn, signOut } from 'next-auth/react';\nimport { useOAuth42Session, useOAuth42User } from './hooks';\n\nexport interface SignInButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign in button component\n */\nexport function SignInButton({ \n children = 'Sign in with OAuth42', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignInButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signIn('oauth42', { callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface SignOutButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign out button component\n */\nexport function SignOutButton({ \n children = 'Sign out', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignOutButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signOut({ callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface UserProfileProps {\n className?: string;\n showEmail?: boolean;\n showName?: boolean;\n showImage?: boolean;\n loadingComponent?: React.ReactNode;\n notAuthenticatedComponent?: React.ReactNode;\n}\n\n/**\n * User profile display component\n */\nexport function UserProfile({\n className = '',\n showEmail = true,\n showName = true,\n showImage = true,\n loadingComponent = <div>Loading...</div>,\n notAuthenticatedComponent = <div>Not authenticated</div>,\n}: UserProfileProps) {\n const { session, loading, isAuthenticated } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated || !session?.user) {\n return <>{notAuthenticatedComponent}</>;\n }\n \n const { user } = session;\n \n return (\n <div className={className}>\n {showImage && user.image && (\n <img \n src={user.image} \n alt={user.name || 'User'} \n style={{ width: 50, height: 50, borderRadius: '50%' }}\n />\n )}\n {showName && user.name && <div>{user.name}</div>}\n {showEmail && user.email && <div>{user.email}</div>}\n </div>\n );\n}\n\nexport interface AuthStatusProps {\n authenticatedComponent?: React.ReactNode;\n unauthenticatedComponent?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Conditional rendering based on auth status\n */\nexport function AuthStatus({\n authenticatedComponent,\n unauthenticatedComponent,\n loadingComponent = <div>Loading...</div>,\n}: AuthStatusProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n return <>{isAuthenticated ? authenticatedComponent : unauthenticatedComponent}</>;\n}\n\nexport interface ProtectedComponentProps {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Wrapper component for protected content\n */\nexport function ProtectedComponent({\n children,\n fallback = <SignInButton />,\n loadingComponent = <div>Loading...</div>,\n}: ProtectedComponentProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}"],"mappings":"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","names":["import_react","import_react","import_react"]}
1
+ {"version":3,"sources":["../../src/client/index.ts","../../src/client/hooks.ts","../../src/client/components.tsx","../../src/utils/hosted-auth.ts"],"sourcesContent":["// Client-side exports\n\n// Re-export commonly used next-auth/react functions\nexport { signIn, signOut, useSession, SessionProvider } from 'next-auth/react';\nexport type { Session } from 'next-auth';\nexport {\n useOAuth42Session,\n useOAuth42User,\n useOAuth42Tokens,\n useRequireAuth,\n} from './hooks';\n\nexport type {\n OAuth42Session,\n UseOAuth42SessionReturn,\n} from './hooks';\n\nexport {\n SignInButton,\n SignOutButton,\n UserProfile,\n AuthStatus,\n ProtectedComponent,\n} from './components';\n\nexport type {\n SignInButtonProps,\n SignOutButtonProps,\n UserProfileProps,\n AuthStatusProps,\n ProtectedComponentProps,\n} from './components';\n\n// Hosted auth utilities\nexport {\n redirectToHostedAuth,\n verifyState,\n DEFAULT_HOSTED_AUTH_CONFIG,\n} from '../utils/hosted-auth';\n\nexport type {\n HostedAuthOptions,\n HostedAuthConfig,\n} from '../utils/hosted-auth';","import { useSession, signIn, signOut } from 'next-auth/react';\nimport { useCallback, useEffect, useState } from 'react';\n\nexport type OAuth42Session<E = {}> = ({\n user?: {\n email?: string | null;\n name?: string | null;\n image?: string | null;\n username?: string;\n emailVerified?: boolean;\n };\n accessToken?: string;\n idToken?: string;\n expires?: string;\n}) & E;\n\nexport interface UseOAuth42SessionReturn<E = {}> {\n session: OAuth42Session<E> | null;\n loading: boolean;\n error: Error | null;\n isAuthenticated: boolean;\n signIn: () => Promise<void>;\n signOut: () => Promise<void>;\n}\n\n/**\n * Hook to manage OAuth42 session with optional extra fields\n */\nexport function useOAuth42Session<E = {}>(): UseOAuth42SessionReturn<E> {\n const { data: session, status } = useSession();\n const [error, setError] = useState<Error | null>(null);\n \n const handleSignIn = useCallback(async () => {\n try {\n setError(null);\n await signIn('oauth42');\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n const handleSignOut = useCallback(async () => {\n try {\n setError(null);\n await signOut();\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n return {\n session: session as unknown as OAuth42Session<E> | null,\n loading: status === 'loading',\n error,\n isAuthenticated: status === 'authenticated',\n signIn: handleSignIn,\n signOut: handleSignOut,\n };\n}\n\n/**\n * Hook to get the current OAuth42 user\n */\nexport function useOAuth42User<E = {}>() {\n const { session, isAuthenticated } = useOAuth42Session<E>();\n \n return {\n user: isAuthenticated ? session?.user : null,\n isAuthenticated,\n };\n}\n\n/**\n * Hook to manage OAuth42 tokens\n */\nexport function useOAuth42Tokens<E = {}>() {\n const { session } = useOAuth42Session<E>();\n const [isExpired, setIsExpired] = useState(false);\n \n useEffect(() => {\n if (session?.expires) {\n const expiryTime = new Date(session.expires).getTime();\n const now = Date.now();\n setIsExpired(now >= expiryTime);\n \n // Set a timer to update expiry status\n const timeUntilExpiry = expiryTime - now;\n if (timeUntilExpiry > 0) {\n const timer = setTimeout(() => {\n setIsExpired(true);\n }, timeUntilExpiry);\n \n return () => clearTimeout(timer);\n }\n }\n }, [session?.expires]);\n \n return {\n accessToken: session?.accessToken,\n idToken: session?.idToken,\n isExpired,\n refreshToken: async () => {\n // Trigger a session refresh\n await signIn('oauth42');\n },\n };\n}\n\n/**\n * Hook for protected routes\n */\nexport function useRequireAuth(redirectTo: string = '/auth/signin') {\n const { isAuthenticated, loading } = useOAuth42Session();\n const [isRedirecting, setIsRedirecting] = useState(false);\n \n useEffect(() => {\n if (!loading && !isAuthenticated && !isRedirecting) {\n setIsRedirecting(true);\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [isAuthenticated, loading, redirectTo, isRedirecting]);\n \n return {\n isAuthenticated,\n loading: loading || isRedirecting,\n };\n}\n","import React from 'react';\nimport { signIn, signOut } from 'next-auth/react';\nimport { useOAuth42Session, useOAuth42User } from './hooks';\n\nexport interface SignInButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign in button component\n */\nexport function SignInButton({ \n children = 'Sign in with OAuth42', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignInButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signIn('oauth42', { callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface SignOutButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign out button component\n */\nexport function SignOutButton({ \n children = 'Sign out', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignOutButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signOut({ callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface UserProfileProps {\n className?: string;\n showEmail?: boolean;\n showName?: boolean;\n showImage?: boolean;\n loadingComponent?: React.ReactNode;\n notAuthenticatedComponent?: React.ReactNode;\n}\n\n/**\n * User profile display component\n */\nexport function UserProfile({\n className = '',\n showEmail = true,\n showName = true,\n showImage = true,\n loadingComponent = <div>Loading...</div>,\n notAuthenticatedComponent = <div>Not authenticated</div>,\n}: UserProfileProps) {\n const { session, loading, isAuthenticated } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated || !session?.user) {\n return <>{notAuthenticatedComponent}</>;\n }\n \n const { user } = session;\n \n return (\n <div className={className}>\n {showImage && user.image && (\n <img \n src={user.image} \n alt={user.name || 'User'} \n style={{ width: 50, height: 50, borderRadius: '50%' }}\n />\n )}\n {showName && user.name && <div>{user.name}</div>}\n {showEmail && user.email && <div>{user.email}</div>}\n </div>\n );\n}\n\nexport interface AuthStatusProps {\n authenticatedComponent?: React.ReactNode;\n unauthenticatedComponent?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Conditional rendering based on auth status\n */\nexport function AuthStatus({\n authenticatedComponent,\n unauthenticatedComponent,\n loadingComponent = <div>Loading...</div>,\n}: AuthStatusProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n return <>{isAuthenticated ? authenticatedComponent : unauthenticatedComponent}</>;\n}\n\nexport interface ProtectedComponentProps {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Wrapper component for protected content\n */\nexport function ProtectedComponent({\n children,\n fallback = <SignInButton />,\n loadingComponent = <div>Loading...</div>,\n}: ProtectedComponentProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}","/**\n * Utilities for OAuth42 Hosted Authentication\n */\n\nexport interface HostedAuthOptions {\n /** OAuth2 client ID */\n clientId: string;\n /** Redirect URI after authentication */\n redirectUri: string;\n /** OAuth2 scopes (space-separated) */\n scope?: string;\n /** OAuth2 state parameter for CSRF protection */\n state?: string;\n /** Base URL for hosted auth (defaults to production) */\n baseUrl?: string;\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Redirect to OAuth42 hosted authentication pages\n *\n * @example\n * ```ts\n * import { redirectToHostedAuth } from '@oauth42/next/client';\n *\n * function LoginButton() {\n * return (\n * <button onClick={() => redirectToHostedAuth({\n * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID!,\n * redirectUri: `${window.location.origin}/api/auth/callback`,\n * })}>\n * Sign in with OAuth42\n * </button>\n * );\n * }\n * ```\n */\nexport function redirectToHostedAuth(options: HostedAuthOptions): void {\n const {\n clientId,\n redirectUri,\n scope = 'openid profile email',\n state = generateState(),\n baseUrl = 'https://hosted-auth.oauth42.com',\n } = options;\n\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope,\n state,\n });\n\n const authUrl = `${baseUrl}/login?${params.toString()}`;\n\n // Store state in sessionStorage for verification on callback\n if (typeof window !== 'undefined') {\n sessionStorage.setItem('oauth42_state', state);\n window.location.href = authUrl;\n }\n}\n\n/**\n * Verify state parameter on OAuth2 callback\n * Call this in your callback page to verify the state matches\n *\n * @example\n * ```ts\n * import { verifyState } from '@oauth42/next/client';\n *\n * export default function CallbackPage() {\n * const searchParams = useSearchParams();\n * const state = searchParams.get('state');\n *\n * if (!verifyState(state)) {\n * return <div>Invalid state parameter</div>;\n * }\n *\n * // Continue with token exchange...\n * }\n * ```\n */\nexport function verifyState(state: string | null): boolean {\n if (typeof window === 'undefined') return false;\n if (!state) return false;\n\n const storedState = sessionStorage.getItem('oauth42_state');\n sessionStorage.removeItem('oauth42_state');\n\n return storedState === state;\n}\n\n/**\n * Configuration for hosted authentication\n */\nexport interface HostedAuthConfig {\n /** Enable hosted authentication */\n enabled: boolean;\n /** Base URL for hosted auth pages (optional, defaults to production) */\n baseUrl?: string;\n /** Feature flags for hosted auth */\n features?: {\n /** Allow user signup */\n signup?: boolean;\n /** Allow social login */\n socialLogin?: boolean;\n /** Allow password reset */\n passwordReset?: boolean;\n };\n}\n\n/**\n * Default hosted auth configuration\n */\nexport const DEFAULT_HOSTED_AUTH_CONFIG: HostedAuthConfig = {\n enabled: true,\n baseUrl: 'https://hosted-auth.oauth42.com',\n features: {\n signup: true,\n socialLogin: false,\n passwordReset: true,\n },\n};\n"],"mappings":"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","names":["import_react","import_react","import_react"]}
@@ -179,19 +179,66 @@ function ProtectedComponent({
179
179
  }
180
180
  return /* @__PURE__ */ jsx(Fragment, { children });
181
181
  }
182
+
183
+ // src/utils/hosted-auth.ts
184
+ function generateState() {
185
+ const array = new Uint8Array(32);
186
+ crypto.getRandomValues(array);
187
+ return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
188
+ }
189
+ function redirectToHostedAuth(options) {
190
+ const {
191
+ clientId,
192
+ redirectUri,
193
+ scope = "openid profile email",
194
+ state = generateState(),
195
+ baseUrl = "https://hosted-auth.oauth42.com"
196
+ } = options;
197
+ const params = new URLSearchParams({
198
+ client_id: clientId,
199
+ redirect_uri: redirectUri,
200
+ response_type: "code",
201
+ scope,
202
+ state
203
+ });
204
+ const authUrl = `${baseUrl}/login?${params.toString()}`;
205
+ if (typeof window !== "undefined") {
206
+ sessionStorage.setItem("oauth42_state", state);
207
+ window.location.href = authUrl;
208
+ }
209
+ }
210
+ function verifyState(state) {
211
+ if (typeof window === "undefined") return false;
212
+ if (!state) return false;
213
+ const storedState = sessionStorage.getItem("oauth42_state");
214
+ sessionStorage.removeItem("oauth42_state");
215
+ return storedState === state;
216
+ }
217
+ var DEFAULT_HOSTED_AUTH_CONFIG = {
218
+ enabled: true,
219
+ baseUrl: "https://hosted-auth.oauth42.com",
220
+ features: {
221
+ signup: true,
222
+ socialLogin: false,
223
+ passwordReset: true
224
+ }
225
+ };
182
226
  export {
183
227
  AuthStatus,
228
+ DEFAULT_HOSTED_AUTH_CONFIG,
184
229
  ProtectedComponent,
185
230
  SessionProvider,
186
231
  SignInButton,
187
232
  SignOutButton,
188
233
  UserProfile,
234
+ redirectToHostedAuth,
189
235
  signIn3 as signIn,
190
236
  signOut3 as signOut,
191
237
  useOAuth42Session,
192
238
  useOAuth42Tokens,
193
239
  useOAuth42User,
194
240
  useRequireAuth,
195
- useSession2 as useSession
241
+ useSession2 as useSession,
242
+ verifyState
196
243
  };
197
244
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts","../../src/client/hooks.ts","../../src/client/components.tsx"],"sourcesContent":["// Client-side exports\n\n// Re-export commonly used next-auth/react functions\nexport { signIn, signOut, useSession, SessionProvider } from 'next-auth/react';\nexport type { Session } from 'next-auth';\nexport {\n useOAuth42Session,\n useOAuth42User,\n useOAuth42Tokens,\n useRequireAuth,\n} from './hooks';\n\nexport type {\n OAuth42Session,\n UseOAuth42SessionReturn,\n} from './hooks';\n\nexport {\n SignInButton,\n SignOutButton,\n UserProfile,\n AuthStatus,\n ProtectedComponent,\n} from './components';\n\nexport type {\n SignInButtonProps,\n SignOutButtonProps,\n UserProfileProps,\n AuthStatusProps,\n ProtectedComponentProps,\n} from './components';","import { useSession, signIn, signOut } from 'next-auth/react';\nimport { useCallback, useEffect, useState } from 'react';\n\nexport type OAuth42Session<E = {}> = ({\n user?: {\n email?: string | null;\n name?: string | null;\n image?: string | null;\n username?: string;\n emailVerified?: boolean;\n };\n accessToken?: string;\n idToken?: string;\n expires?: string;\n}) & E;\n\nexport interface UseOAuth42SessionReturn<E = {}> {\n session: OAuth42Session<E> | null;\n loading: boolean;\n error: Error | null;\n isAuthenticated: boolean;\n signIn: () => Promise<void>;\n signOut: () => Promise<void>;\n}\n\n/**\n * Hook to manage OAuth42 session with optional extra fields\n */\nexport function useOAuth42Session<E = {}>(): UseOAuth42SessionReturn<E> {\n const { data: session, status } = useSession();\n const [error, setError] = useState<Error | null>(null);\n \n const handleSignIn = useCallback(async () => {\n try {\n setError(null);\n await signIn('oauth42');\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n const handleSignOut = useCallback(async () => {\n try {\n setError(null);\n await signOut();\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n return {\n session: session as unknown as OAuth42Session<E> | null,\n loading: status === 'loading',\n error,\n isAuthenticated: status === 'authenticated',\n signIn: handleSignIn,\n signOut: handleSignOut,\n };\n}\n\n/**\n * Hook to get the current OAuth42 user\n */\nexport function useOAuth42User<E = {}>() {\n const { session, isAuthenticated } = useOAuth42Session<E>();\n \n return {\n user: isAuthenticated ? session?.user : null,\n isAuthenticated,\n };\n}\n\n/**\n * Hook to manage OAuth42 tokens\n */\nexport function useOAuth42Tokens<E = {}>() {\n const { session } = useOAuth42Session<E>();\n const [isExpired, setIsExpired] = useState(false);\n \n useEffect(() => {\n if (session?.expires) {\n const expiryTime = new Date(session.expires).getTime();\n const now = Date.now();\n setIsExpired(now >= expiryTime);\n \n // Set a timer to update expiry status\n const timeUntilExpiry = expiryTime - now;\n if (timeUntilExpiry > 0) {\n const timer = setTimeout(() => {\n setIsExpired(true);\n }, timeUntilExpiry);\n \n return () => clearTimeout(timer);\n }\n }\n }, [session?.expires]);\n \n return {\n accessToken: session?.accessToken,\n idToken: session?.idToken,\n isExpired,\n refreshToken: async () => {\n // Trigger a session refresh\n await signIn('oauth42');\n },\n };\n}\n\n/**\n * Hook for protected routes\n */\nexport function useRequireAuth(redirectTo: string = '/auth/signin') {\n const { isAuthenticated, loading } = useOAuth42Session();\n const [isRedirecting, setIsRedirecting] = useState(false);\n \n useEffect(() => {\n if (!loading && !isAuthenticated && !isRedirecting) {\n setIsRedirecting(true);\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [isAuthenticated, loading, redirectTo, isRedirecting]);\n \n return {\n isAuthenticated,\n loading: loading || isRedirecting,\n };\n}\n","import React from 'react';\nimport { signIn, signOut } from 'next-auth/react';\nimport { useOAuth42Session, useOAuth42User } from './hooks';\n\nexport interface SignInButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign in button component\n */\nexport function SignInButton({ \n children = 'Sign in with OAuth42', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignInButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signIn('oauth42', { callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface SignOutButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign out button component\n */\nexport function SignOutButton({ \n children = 'Sign out', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignOutButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signOut({ callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface UserProfileProps {\n className?: string;\n showEmail?: boolean;\n showName?: boolean;\n showImage?: boolean;\n loadingComponent?: React.ReactNode;\n notAuthenticatedComponent?: React.ReactNode;\n}\n\n/**\n * User profile display component\n */\nexport function UserProfile({\n className = '',\n showEmail = true,\n showName = true,\n showImage = true,\n loadingComponent = <div>Loading...</div>,\n notAuthenticatedComponent = <div>Not authenticated</div>,\n}: UserProfileProps) {\n const { session, loading, isAuthenticated } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated || !session?.user) {\n return <>{notAuthenticatedComponent}</>;\n }\n \n const { user } = session;\n \n return (\n <div className={className}>\n {showImage && user.image && (\n <img \n src={user.image} \n alt={user.name || 'User'} \n style={{ width: 50, height: 50, borderRadius: '50%' }}\n />\n )}\n {showName && user.name && <div>{user.name}</div>}\n {showEmail && user.email && <div>{user.email}</div>}\n </div>\n );\n}\n\nexport interface AuthStatusProps {\n authenticatedComponent?: React.ReactNode;\n unauthenticatedComponent?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Conditional rendering based on auth status\n */\nexport function AuthStatus({\n authenticatedComponent,\n unauthenticatedComponent,\n loadingComponent = <div>Loading...</div>,\n}: AuthStatusProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n return <>{isAuthenticated ? authenticatedComponent : unauthenticatedComponent}</>;\n}\n\nexport interface ProtectedComponentProps {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Wrapper component for protected content\n */\nexport function ProtectedComponent({\n children,\n fallback = <SignInButton />,\n loadingComponent = <div>Loading...</div>,\n}: ProtectedComponentProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}"],"mappings":"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","names":["signIn","signOut","useSession","signIn","signOut","signIn","signOut"]}
1
+ {"version":3,"sources":["../../src/client/index.ts","../../src/client/hooks.ts","../../src/client/components.tsx","../../src/utils/hosted-auth.ts"],"sourcesContent":["// Client-side exports\n\n// Re-export commonly used next-auth/react functions\nexport { signIn, signOut, useSession, SessionProvider } from 'next-auth/react';\nexport type { Session } from 'next-auth';\nexport {\n useOAuth42Session,\n useOAuth42User,\n useOAuth42Tokens,\n useRequireAuth,\n} from './hooks';\n\nexport type {\n OAuth42Session,\n UseOAuth42SessionReturn,\n} from './hooks';\n\nexport {\n SignInButton,\n SignOutButton,\n UserProfile,\n AuthStatus,\n ProtectedComponent,\n} from './components';\n\nexport type {\n SignInButtonProps,\n SignOutButtonProps,\n UserProfileProps,\n AuthStatusProps,\n ProtectedComponentProps,\n} from './components';\n\n// Hosted auth utilities\nexport {\n redirectToHostedAuth,\n verifyState,\n DEFAULT_HOSTED_AUTH_CONFIG,\n} from '../utils/hosted-auth';\n\nexport type {\n HostedAuthOptions,\n HostedAuthConfig,\n} from '../utils/hosted-auth';","import { useSession, signIn, signOut } from 'next-auth/react';\nimport { useCallback, useEffect, useState } from 'react';\n\nexport type OAuth42Session<E = {}> = ({\n user?: {\n email?: string | null;\n name?: string | null;\n image?: string | null;\n username?: string;\n emailVerified?: boolean;\n };\n accessToken?: string;\n idToken?: string;\n expires?: string;\n}) & E;\n\nexport interface UseOAuth42SessionReturn<E = {}> {\n session: OAuth42Session<E> | null;\n loading: boolean;\n error: Error | null;\n isAuthenticated: boolean;\n signIn: () => Promise<void>;\n signOut: () => Promise<void>;\n}\n\n/**\n * Hook to manage OAuth42 session with optional extra fields\n */\nexport function useOAuth42Session<E = {}>(): UseOAuth42SessionReturn<E> {\n const { data: session, status } = useSession();\n const [error, setError] = useState<Error | null>(null);\n \n const handleSignIn = useCallback(async () => {\n try {\n setError(null);\n await signIn('oauth42');\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n const handleSignOut = useCallback(async () => {\n try {\n setError(null);\n await signOut();\n } catch (err) {\n setError(err as Error);\n }\n }, []);\n \n return {\n session: session as unknown as OAuth42Session<E> | null,\n loading: status === 'loading',\n error,\n isAuthenticated: status === 'authenticated',\n signIn: handleSignIn,\n signOut: handleSignOut,\n };\n}\n\n/**\n * Hook to get the current OAuth42 user\n */\nexport function useOAuth42User<E = {}>() {\n const { session, isAuthenticated } = useOAuth42Session<E>();\n \n return {\n user: isAuthenticated ? session?.user : null,\n isAuthenticated,\n };\n}\n\n/**\n * Hook to manage OAuth42 tokens\n */\nexport function useOAuth42Tokens<E = {}>() {\n const { session } = useOAuth42Session<E>();\n const [isExpired, setIsExpired] = useState(false);\n \n useEffect(() => {\n if (session?.expires) {\n const expiryTime = new Date(session.expires).getTime();\n const now = Date.now();\n setIsExpired(now >= expiryTime);\n \n // Set a timer to update expiry status\n const timeUntilExpiry = expiryTime - now;\n if (timeUntilExpiry > 0) {\n const timer = setTimeout(() => {\n setIsExpired(true);\n }, timeUntilExpiry);\n \n return () => clearTimeout(timer);\n }\n }\n }, [session?.expires]);\n \n return {\n accessToken: session?.accessToken,\n idToken: session?.idToken,\n isExpired,\n refreshToken: async () => {\n // Trigger a session refresh\n await signIn('oauth42');\n },\n };\n}\n\n/**\n * Hook for protected routes\n */\nexport function useRequireAuth(redirectTo: string = '/auth/signin') {\n const { isAuthenticated, loading } = useOAuth42Session();\n const [isRedirecting, setIsRedirecting] = useState(false);\n \n useEffect(() => {\n if (!loading && !isAuthenticated && !isRedirecting) {\n setIsRedirecting(true);\n if (typeof window !== 'undefined') {\n window.location.href = redirectTo;\n }\n }\n }, [isAuthenticated, loading, redirectTo, isRedirecting]);\n \n return {\n isAuthenticated,\n loading: loading || isRedirecting,\n };\n}\n","import React from 'react';\nimport { signIn, signOut } from 'next-auth/react';\nimport { useOAuth42Session, useOAuth42User } from './hooks';\n\nexport interface SignInButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign in button component\n */\nexport function SignInButton({ \n children = 'Sign in with OAuth42', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignInButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signIn('oauth42', { callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface SignOutButtonProps {\n children?: React.ReactNode;\n className?: string;\n callbackUrl?: string;\n onClick?: () => void;\n}\n\n/**\n * Sign out button component\n */\nexport function SignOutButton({ \n children = 'Sign out', \n className = '',\n callbackUrl = '/',\n onClick\n}: SignOutButtonProps) {\n const handleClick = async () => {\n if (onClick) onClick();\n await signOut({ callbackUrl });\n };\n \n return (\n <button\n onClick={handleClick}\n className={className}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nexport interface UserProfileProps {\n className?: string;\n showEmail?: boolean;\n showName?: boolean;\n showImage?: boolean;\n loadingComponent?: React.ReactNode;\n notAuthenticatedComponent?: React.ReactNode;\n}\n\n/**\n * User profile display component\n */\nexport function UserProfile({\n className = '',\n showEmail = true,\n showName = true,\n showImage = true,\n loadingComponent = <div>Loading...</div>,\n notAuthenticatedComponent = <div>Not authenticated</div>,\n}: UserProfileProps) {\n const { session, loading, isAuthenticated } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated || !session?.user) {\n return <>{notAuthenticatedComponent}</>;\n }\n \n const { user } = session;\n \n return (\n <div className={className}>\n {showImage && user.image && (\n <img \n src={user.image} \n alt={user.name || 'User'} \n style={{ width: 50, height: 50, borderRadius: '50%' }}\n />\n )}\n {showName && user.name && <div>{user.name}</div>}\n {showEmail && user.email && <div>{user.email}</div>}\n </div>\n );\n}\n\nexport interface AuthStatusProps {\n authenticatedComponent?: React.ReactNode;\n unauthenticatedComponent?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Conditional rendering based on auth status\n */\nexport function AuthStatus({\n authenticatedComponent,\n unauthenticatedComponent,\n loadingComponent = <div>Loading...</div>,\n}: AuthStatusProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n return <>{isAuthenticated ? authenticatedComponent : unauthenticatedComponent}</>;\n}\n\nexport interface ProtectedComponentProps {\n children: React.ReactNode;\n fallback?: React.ReactNode;\n loadingComponent?: React.ReactNode;\n}\n\n/**\n * Wrapper component for protected content\n */\nexport function ProtectedComponent({\n children,\n fallback = <SignInButton />,\n loadingComponent = <div>Loading...</div>,\n}: ProtectedComponentProps) {\n const { isAuthenticated, loading } = useOAuth42Session();\n \n if (loading) {\n return <>{loadingComponent}</>;\n }\n \n if (!isAuthenticated) {\n return <>{fallback}</>;\n }\n \n return <>{children}</>;\n}","/**\n * Utilities for OAuth42 Hosted Authentication\n */\n\nexport interface HostedAuthOptions {\n /** OAuth2 client ID */\n clientId: string;\n /** Redirect URI after authentication */\n redirectUri: string;\n /** OAuth2 scopes (space-separated) */\n scope?: string;\n /** OAuth2 state parameter for CSRF protection */\n state?: string;\n /** Base URL for hosted auth (defaults to production) */\n baseUrl?: string;\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nfunction generateState(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Redirect to OAuth42 hosted authentication pages\n *\n * @example\n * ```ts\n * import { redirectToHostedAuth } from '@oauth42/next/client';\n *\n * function LoginButton() {\n * return (\n * <button onClick={() => redirectToHostedAuth({\n * clientId: process.env.NEXT_PUBLIC_OAUTH42_CLIENT_ID!,\n * redirectUri: `${window.location.origin}/api/auth/callback`,\n * })}>\n * Sign in with OAuth42\n * </button>\n * );\n * }\n * ```\n */\nexport function redirectToHostedAuth(options: HostedAuthOptions): void {\n const {\n clientId,\n redirectUri,\n scope = 'openid profile email',\n state = generateState(),\n baseUrl = 'https://hosted-auth.oauth42.com',\n } = options;\n\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n scope,\n state,\n });\n\n const authUrl = `${baseUrl}/login?${params.toString()}`;\n\n // Store state in sessionStorage for verification on callback\n if (typeof window !== 'undefined') {\n sessionStorage.setItem('oauth42_state', state);\n window.location.href = authUrl;\n }\n}\n\n/**\n * Verify state parameter on OAuth2 callback\n * Call this in your callback page to verify the state matches\n *\n * @example\n * ```ts\n * import { verifyState } from '@oauth42/next/client';\n *\n * export default function CallbackPage() {\n * const searchParams = useSearchParams();\n * const state = searchParams.get('state');\n *\n * if (!verifyState(state)) {\n * return <div>Invalid state parameter</div>;\n * }\n *\n * // Continue with token exchange...\n * }\n * ```\n */\nexport function verifyState(state: string | null): boolean {\n if (typeof window === 'undefined') return false;\n if (!state) return false;\n\n const storedState = sessionStorage.getItem('oauth42_state');\n sessionStorage.removeItem('oauth42_state');\n\n return storedState === state;\n}\n\n/**\n * Configuration for hosted authentication\n */\nexport interface HostedAuthConfig {\n /** Enable hosted authentication */\n enabled: boolean;\n /** Base URL for hosted auth pages (optional, defaults to production) */\n baseUrl?: string;\n /** Feature flags for hosted auth */\n features?: {\n /** Allow user signup */\n signup?: boolean;\n /** Allow social login */\n socialLogin?: boolean;\n /** Allow password reset */\n passwordReset?: boolean;\n };\n}\n\n/**\n * Default hosted auth configuration\n */\nexport const DEFAULT_HOSTED_AUTH_CONFIG: HostedAuthConfig = {\n enabled: true,\n baseUrl: 'https://hosted-auth.oauth42.com',\n features: {\n signup: true,\n socialLogin: false,\n passwordReset: true,\n },\n};\n"],"mappings":"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","names":["signIn","signOut","useSession","signIn","signOut","signIn","signOut"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oauth42/next",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Official OAuth42 SDK for Next.js applications",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -76,4 +76,4 @@
76
76
  "url": "https://github.com/devxpod/oauth42/issues"
77
77
  },
78
78
  "homepage": "https://oauth42.com"
79
- }
79
+ }