@marcwelti/mw-core 0.2.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 +141 -0
- package/dist/context/index.d.mts +95 -0
- package/dist/context/index.d.ts +95 -0
- package/dist/context/index.js +280 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/index.mjs +276 -0
- package/dist/context/index.mjs.map +1 -0
- package/dist/firebase/index.d.mts +176 -0
- package/dist/firebase/index.d.ts +176 -0
- package/dist/firebase/index.js +393 -0
- package/dist/firebase/index.js.map +1 -0
- package/dist/firebase/index.mjs +318 -0
- package/dist/firebase/index.mjs.map +1 -0
- package/dist/hooks/index.d.mts +97 -0
- package/dist/hooks/index.d.ts +97 -0
- package/dist/hooks/index.js +618 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +611 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/index.d.mts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +922 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +843 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server/index.d.mts +216 -0
- package/dist/server/index.d.ts +216 -0
- package/dist/server/index.js +309 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +276 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/storage-BU_rfYCi.d.mts +43 -0
- package/dist/storage-BU_rfYCi.d.ts +43 -0
- package/dist/types/index.d.mts +108 -0
- package/dist/types/index.d.ts +108 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.mjs +3 -0
- package/dist/types/index.mjs.map +1 -0
- package/package.json +91 -0
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# @marcwelti/mw-core
|
|
2
|
+
|
|
3
|
+
Shared Firebase utilities, React hooks, and server-side authentication for Marc Welti applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @marcwelti/mw-core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
### Client-Side (Browser)
|
|
14
|
+
|
|
15
|
+
- **Firebase Auth**: Sign in with email/password, Google OAuth
|
|
16
|
+
- **Firestore**: Document and collection utilities
|
|
17
|
+
- **Storage**: File upload/download helpers
|
|
18
|
+
- **React Hooks**: `useAuth`, `useFirestore`, `useStorage`
|
|
19
|
+
- **Auth Context**: `AuthProvider` for React apps
|
|
20
|
+
|
|
21
|
+
### Server-Side (Node.js / Next.js API Routes)
|
|
22
|
+
|
|
23
|
+
- **Firebase Admin SDK**: Server-side Firebase access
|
|
24
|
+
- **Session Cookies**: Cross-subdomain session management
|
|
25
|
+
- **Role Checking**: `hasRole()`, `isStaff()`, `isAdmin()`, `isOwner()`
|
|
26
|
+
- **Tier Checking**: `hasAccess()`, `hasSubscription()`
|
|
27
|
+
- **Middleware Helpers**: Auth verification for Next.js
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Client-Side
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// Firebase auth
|
|
35
|
+
import { signInWithEmail, signInWithGoogle, signOut } from '@marcwelti/mw-core/firebase';
|
|
36
|
+
|
|
37
|
+
// React hooks
|
|
38
|
+
import { useAuth } from '@marcwelti/mw-core/hooks';
|
|
39
|
+
|
|
40
|
+
// Auth context
|
|
41
|
+
import { AuthProvider, useAuthContext } from '@marcwelti/mw-core/context';
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Server-Side (API Routes)
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import {
|
|
48
|
+
handleLogin,
|
|
49
|
+
handleLogout,
|
|
50
|
+
handleSession,
|
|
51
|
+
createSessionConfig
|
|
52
|
+
} from '@marcwelti/mw-core/server';
|
|
53
|
+
|
|
54
|
+
// In your API route
|
|
55
|
+
export async function POST(request: Request) {
|
|
56
|
+
const { idToken } = await request.json();
|
|
57
|
+
const sessionConfig = createSessionConfig('.marc-welti.ch');
|
|
58
|
+
const cookieHeader = await handleLogin(idToken, sessionConfig);
|
|
59
|
+
|
|
60
|
+
return new Response(JSON.stringify({ success: true }), {
|
|
61
|
+
headers: { 'Set-Cookie': cookieHeader }
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Role & Tier Checking
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { hasRole, isStaff, hasAccess } from '@marcwelti/mw-core/server';
|
|
70
|
+
|
|
71
|
+
// Check if user has required role
|
|
72
|
+
if (hasRole(user.role, ['owner', 'admin'])) {
|
|
73
|
+
// Grant access
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check if user is staff (owner, admin, or employee)
|
|
77
|
+
if (isStaff(user.role)) {
|
|
78
|
+
// Show staff features
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Check subscription tier access
|
|
82
|
+
if (hasAccess(user.tier, ['MasterClass', 'PremiumOnlineKurs'])) {
|
|
83
|
+
// Show premium content
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Environment Variables
|
|
88
|
+
|
|
89
|
+
### Client-Side (NEXT_PUBLIC_*)
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
NEXT_PUBLIC_FIREBASE_API_KEY=
|
|
93
|
+
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
|
|
94
|
+
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
|
|
95
|
+
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
|
|
96
|
+
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
|
|
97
|
+
NEXT_PUBLIC_FIREBASE_APP_ID=
|
|
98
|
+
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Server-Side (Secret)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
FIREBASE_ADMIN_PROJECT_ID=
|
|
105
|
+
FIREBASE_ADMIN_CLIENT_EMAIL=
|
|
106
|
+
FIREBASE_ADMIN_PRIVATE_KEY=
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Subscription Tiers
|
|
110
|
+
|
|
111
|
+
- `MasterClass`
|
|
112
|
+
- `MasterClassLight`
|
|
113
|
+
- `PremiumOnlineKurs`
|
|
114
|
+
- `MasterOnlineKurs`
|
|
115
|
+
- `OnlineKursPro`
|
|
116
|
+
- `PremiumTraining`
|
|
117
|
+
- `VIPTraining`
|
|
118
|
+
|
|
119
|
+
## User Roles
|
|
120
|
+
|
|
121
|
+
- `owner` - Marc only, full system access
|
|
122
|
+
- `employee` - Team members, limited to assigned customers
|
|
123
|
+
- `admin` - External developers/support, full technical access
|
|
124
|
+
- `user` - Customers, customer-facing features only
|
|
125
|
+
|
|
126
|
+
## Development
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Install dependencies
|
|
130
|
+
npm install
|
|
131
|
+
|
|
132
|
+
# Build
|
|
133
|
+
npm run build
|
|
134
|
+
|
|
135
|
+
# Watch mode
|
|
136
|
+
npm run dev
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
UNLICENSED - Private package for Marc Welti applications only.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
import { User, UserCredential } from 'firebase/auth';
|
|
4
|
+
|
|
5
|
+
interface AuthContextValue {
|
|
6
|
+
/** The current authenticated user, or null if not authenticated */
|
|
7
|
+
user: User | null;
|
|
8
|
+
/** Whether the auth state is still loading */
|
|
9
|
+
loading: boolean;
|
|
10
|
+
/** Whether the user is authenticated */
|
|
11
|
+
isAuthenticated: boolean;
|
|
12
|
+
/** Any auth error that occurred */
|
|
13
|
+
error: Error | null;
|
|
14
|
+
/** Sign in with email and password */
|
|
15
|
+
signIn: (email: string, password: string) => Promise<UserCredential>;
|
|
16
|
+
/** Create a new account with email and password */
|
|
17
|
+
signUp: (email: string, password: string, displayName?: string) => Promise<UserCredential>;
|
|
18
|
+
/** Sign out the current user */
|
|
19
|
+
signOut: () => Promise<void>;
|
|
20
|
+
/** Sign in with Google */
|
|
21
|
+
signInWithGoogle: () => Promise<UserCredential>;
|
|
22
|
+
/** Send a password reset email */
|
|
23
|
+
resetPassword: (email: string) => Promise<void>;
|
|
24
|
+
/** Update the user's profile */
|
|
25
|
+
updateProfile: (profile: {
|
|
26
|
+
displayName?: string;
|
|
27
|
+
photoURL?: string;
|
|
28
|
+
}) => Promise<void>;
|
|
29
|
+
/** Send email verification */
|
|
30
|
+
sendEmailVerification: () => Promise<void>;
|
|
31
|
+
/** Clear any auth errors */
|
|
32
|
+
clearError: () => void;
|
|
33
|
+
}
|
|
34
|
+
interface AuthProviderProps {
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
/** Optional: Called when auth state changes */
|
|
37
|
+
onAuthStateChange?: (user: User | null) => void;
|
|
38
|
+
/** Optional: Loading component to show while auth state is loading */
|
|
39
|
+
loadingComponent?: ReactNode;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Auth Provider component that wraps your app and provides auth context
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* // app/layout.tsx
|
|
47
|
+
* import { AuthProvider } from '@marcwelti/mw-core';
|
|
48
|
+
*
|
|
49
|
+
* export default function RootLayout({ children }) {
|
|
50
|
+
* return (
|
|
51
|
+
* <html>
|
|
52
|
+
* <body>
|
|
53
|
+
* <AuthProvider>
|
|
54
|
+
* {children}
|
|
55
|
+
* </AuthProvider>
|
|
56
|
+
* </body>
|
|
57
|
+
* </html>
|
|
58
|
+
* );
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
declare function AuthProvider({ children, onAuthStateChange, loadingComponent, }: AuthProviderProps): react_jsx_runtime.JSX.Element;
|
|
63
|
+
/**
|
|
64
|
+
* Hook to access auth context
|
|
65
|
+
* Must be used within an AuthProvider
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* function MyComponent() {
|
|
70
|
+
* const { user, signOut, isAuthenticated } = useAuthContext();
|
|
71
|
+
*
|
|
72
|
+
* if (!isAuthenticated) {
|
|
73
|
+
* return <LoginForm />;
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* return (
|
|
77
|
+
* <div>
|
|
78
|
+
* <p>Welcome, {user?.displayName}</p>
|
|
79
|
+
* <button onClick={signOut}>Sign Out</button>
|
|
80
|
+
* </div>
|
|
81
|
+
* );
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
declare function useAuthContext(): AuthContextValue;
|
|
86
|
+
/**
|
|
87
|
+
* HOC for protecting routes that require authentication
|
|
88
|
+
* Redirects to login or shows loading state
|
|
89
|
+
*/
|
|
90
|
+
declare function withAuth<P extends object>(WrappedComponent: React.ComponentType<P>, options?: {
|
|
91
|
+
LoadingComponent?: React.ComponentType;
|
|
92
|
+
UnauthenticatedComponent?: React.ComponentType;
|
|
93
|
+
}): (props: P) => react_jsx_runtime.JSX.Element;
|
|
94
|
+
|
|
95
|
+
export { type AuthContextValue, AuthProvider, type AuthProviderProps, useAuthContext, withAuth };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
import { User, UserCredential } from 'firebase/auth';
|
|
4
|
+
|
|
5
|
+
interface AuthContextValue {
|
|
6
|
+
/** The current authenticated user, or null if not authenticated */
|
|
7
|
+
user: User | null;
|
|
8
|
+
/** Whether the auth state is still loading */
|
|
9
|
+
loading: boolean;
|
|
10
|
+
/** Whether the user is authenticated */
|
|
11
|
+
isAuthenticated: boolean;
|
|
12
|
+
/** Any auth error that occurred */
|
|
13
|
+
error: Error | null;
|
|
14
|
+
/** Sign in with email and password */
|
|
15
|
+
signIn: (email: string, password: string) => Promise<UserCredential>;
|
|
16
|
+
/** Create a new account with email and password */
|
|
17
|
+
signUp: (email: string, password: string, displayName?: string) => Promise<UserCredential>;
|
|
18
|
+
/** Sign out the current user */
|
|
19
|
+
signOut: () => Promise<void>;
|
|
20
|
+
/** Sign in with Google */
|
|
21
|
+
signInWithGoogle: () => Promise<UserCredential>;
|
|
22
|
+
/** Send a password reset email */
|
|
23
|
+
resetPassword: (email: string) => Promise<void>;
|
|
24
|
+
/** Update the user's profile */
|
|
25
|
+
updateProfile: (profile: {
|
|
26
|
+
displayName?: string;
|
|
27
|
+
photoURL?: string;
|
|
28
|
+
}) => Promise<void>;
|
|
29
|
+
/** Send email verification */
|
|
30
|
+
sendEmailVerification: () => Promise<void>;
|
|
31
|
+
/** Clear any auth errors */
|
|
32
|
+
clearError: () => void;
|
|
33
|
+
}
|
|
34
|
+
interface AuthProviderProps {
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
/** Optional: Called when auth state changes */
|
|
37
|
+
onAuthStateChange?: (user: User | null) => void;
|
|
38
|
+
/** Optional: Loading component to show while auth state is loading */
|
|
39
|
+
loadingComponent?: ReactNode;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Auth Provider component that wraps your app and provides auth context
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* // app/layout.tsx
|
|
47
|
+
* import { AuthProvider } from '@marcwelti/mw-core';
|
|
48
|
+
*
|
|
49
|
+
* export default function RootLayout({ children }) {
|
|
50
|
+
* return (
|
|
51
|
+
* <html>
|
|
52
|
+
* <body>
|
|
53
|
+
* <AuthProvider>
|
|
54
|
+
* {children}
|
|
55
|
+
* </AuthProvider>
|
|
56
|
+
* </body>
|
|
57
|
+
* </html>
|
|
58
|
+
* );
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
declare function AuthProvider({ children, onAuthStateChange, loadingComponent, }: AuthProviderProps): react_jsx_runtime.JSX.Element;
|
|
63
|
+
/**
|
|
64
|
+
* Hook to access auth context
|
|
65
|
+
* Must be used within an AuthProvider
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* function MyComponent() {
|
|
70
|
+
* const { user, signOut, isAuthenticated } = useAuthContext();
|
|
71
|
+
*
|
|
72
|
+
* if (!isAuthenticated) {
|
|
73
|
+
* return <LoginForm />;
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* return (
|
|
77
|
+
* <div>
|
|
78
|
+
* <p>Welcome, {user?.displayName}</p>
|
|
79
|
+
* <button onClick={signOut}>Sign Out</button>
|
|
80
|
+
* </div>
|
|
81
|
+
* );
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
declare function useAuthContext(): AuthContextValue;
|
|
86
|
+
/**
|
|
87
|
+
* HOC for protecting routes that require authentication
|
|
88
|
+
* Redirects to login or shows loading state
|
|
89
|
+
*/
|
|
90
|
+
declare function withAuth<P extends object>(WrappedComponent: React.ComponentType<P>, options?: {
|
|
91
|
+
LoadingComponent?: React.ComponentType;
|
|
92
|
+
UnauthenticatedComponent?: React.ComponentType;
|
|
93
|
+
}): (props: P) => react_jsx_runtime.JSX.Element;
|
|
94
|
+
|
|
95
|
+
export { type AuthContextValue, AuthProvider, type AuthProviderProps, useAuthContext, withAuth };
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var auth = require('firebase/auth');
|
|
5
|
+
var app = require('firebase/app');
|
|
6
|
+
var firestore = require('firebase/firestore');
|
|
7
|
+
var storage = require('firebase/storage');
|
|
8
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
+
|
|
10
|
+
// src/context/AuthContext.tsx
|
|
11
|
+
function getFirebaseConfig() {
|
|
12
|
+
const config = {
|
|
13
|
+
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || "",
|
|
14
|
+
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || "",
|
|
15
|
+
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || "",
|
|
16
|
+
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || "",
|
|
17
|
+
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || "",
|
|
18
|
+
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || "",
|
|
19
|
+
measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID
|
|
20
|
+
};
|
|
21
|
+
const requiredFields = [
|
|
22
|
+
"apiKey",
|
|
23
|
+
"authDomain",
|
|
24
|
+
"projectId",
|
|
25
|
+
"storageBucket",
|
|
26
|
+
"messagingSenderId",
|
|
27
|
+
"appId"
|
|
28
|
+
];
|
|
29
|
+
const missingFields = requiredFields.filter((field) => !config[field]);
|
|
30
|
+
if (missingFields.length > 0) {
|
|
31
|
+
console.warn(
|
|
32
|
+
`[mw-core] Missing Firebase config fields: ${missingFields.join(", ")}. Make sure to set NEXT_PUBLIC_FIREBASE_* environment variables.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return config;
|
|
36
|
+
}
|
|
37
|
+
function initializeFirebase(config) {
|
|
38
|
+
if (app.getApps().length > 0) {
|
|
39
|
+
return app.getApp();
|
|
40
|
+
}
|
|
41
|
+
const firebaseConfig = getFirebaseConfig();
|
|
42
|
+
return app.initializeApp(firebaseConfig);
|
|
43
|
+
}
|
|
44
|
+
var _app = null;
|
|
45
|
+
var _auth = null;
|
|
46
|
+
var _db = null;
|
|
47
|
+
var _storage = null;
|
|
48
|
+
function getFirebaseApp() {
|
|
49
|
+
if (!_app) {
|
|
50
|
+
_app = initializeFirebase();
|
|
51
|
+
}
|
|
52
|
+
return _app;
|
|
53
|
+
}
|
|
54
|
+
function getFirebaseAuth() {
|
|
55
|
+
if (!_auth) {
|
|
56
|
+
_auth = auth.getAuth(getFirebaseApp());
|
|
57
|
+
}
|
|
58
|
+
return _auth;
|
|
59
|
+
}
|
|
60
|
+
function getFirebaseFirestore() {
|
|
61
|
+
if (!_db) {
|
|
62
|
+
_db = firestore.getFirestore(getFirebaseApp());
|
|
63
|
+
}
|
|
64
|
+
return _db;
|
|
65
|
+
}
|
|
66
|
+
function getFirebaseStorage() {
|
|
67
|
+
if (!_storage) {
|
|
68
|
+
_storage = storage.getStorage(getFirebaseApp());
|
|
69
|
+
}
|
|
70
|
+
return _storage;
|
|
71
|
+
}
|
|
72
|
+
typeof window !== "undefined" ? getFirebaseApp() : null;
|
|
73
|
+
typeof window !== "undefined" ? getFirebaseAuth() : null;
|
|
74
|
+
typeof window !== "undefined" ? getFirebaseFirestore() : null;
|
|
75
|
+
typeof window !== "undefined" ? getFirebaseStorage() : null;
|
|
76
|
+
|
|
77
|
+
// src/firebase/auth.ts
|
|
78
|
+
async function signInWithEmail(email, password) {
|
|
79
|
+
const auth2 = getFirebaseAuth();
|
|
80
|
+
return auth.signInWithEmailAndPassword(auth2, email, password);
|
|
81
|
+
}
|
|
82
|
+
async function signUpWithEmail(email, password, displayName) {
|
|
83
|
+
const auth2 = getFirebaseAuth();
|
|
84
|
+
const credential = await auth.createUserWithEmailAndPassword(auth2, email, password);
|
|
85
|
+
if (displayName && credential.user) {
|
|
86
|
+
await auth.updateProfile(credential.user, { displayName });
|
|
87
|
+
}
|
|
88
|
+
return credential;
|
|
89
|
+
}
|
|
90
|
+
async function signOut() {
|
|
91
|
+
const auth2 = getFirebaseAuth();
|
|
92
|
+
return auth.signOut(auth2);
|
|
93
|
+
}
|
|
94
|
+
async function resetPassword(email) {
|
|
95
|
+
const auth2 = getFirebaseAuth();
|
|
96
|
+
return auth.sendPasswordResetEmail(auth2, email);
|
|
97
|
+
}
|
|
98
|
+
async function sendVerificationEmail(user) {
|
|
99
|
+
return auth.sendEmailVerification(user);
|
|
100
|
+
}
|
|
101
|
+
async function updateUserProfile(user, profile) {
|
|
102
|
+
return auth.updateProfile(user, profile);
|
|
103
|
+
}
|
|
104
|
+
async function signInWithGoogle() {
|
|
105
|
+
const auth2 = getFirebaseAuth();
|
|
106
|
+
const provider = new auth.GoogleAuthProvider();
|
|
107
|
+
provider.addScope("email");
|
|
108
|
+
provider.addScope("profile");
|
|
109
|
+
return auth.signInWithPopup(auth2, provider);
|
|
110
|
+
}
|
|
111
|
+
function subscribeToAuthState(callback) {
|
|
112
|
+
const auth2 = getFirebaseAuth();
|
|
113
|
+
return auth.onAuthStateChanged(auth2, callback);
|
|
114
|
+
}
|
|
115
|
+
var AuthContext = react.createContext(void 0);
|
|
116
|
+
function AuthProvider({
|
|
117
|
+
children,
|
|
118
|
+
onAuthStateChange,
|
|
119
|
+
loadingComponent
|
|
120
|
+
}) {
|
|
121
|
+
const [user, setUser] = react.useState(null);
|
|
122
|
+
const [loading, setLoading] = react.useState(true);
|
|
123
|
+
const [error, setError] = react.useState(null);
|
|
124
|
+
react.useEffect(() => {
|
|
125
|
+
const unsubscribe = subscribeToAuthState((user2) => {
|
|
126
|
+
setUser(user2);
|
|
127
|
+
setLoading(false);
|
|
128
|
+
onAuthStateChange?.(user2);
|
|
129
|
+
});
|
|
130
|
+
return unsubscribe;
|
|
131
|
+
}, [onAuthStateChange]);
|
|
132
|
+
const signIn = react.useCallback(async (email, password) => {
|
|
133
|
+
setError(null);
|
|
134
|
+
try {
|
|
135
|
+
return await signInWithEmail(email, password);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
const error2 = err;
|
|
138
|
+
setError(error2);
|
|
139
|
+
throw error2;
|
|
140
|
+
}
|
|
141
|
+
}, []);
|
|
142
|
+
const signUp = react.useCallback(
|
|
143
|
+
async (email, password, displayName) => {
|
|
144
|
+
setError(null);
|
|
145
|
+
try {
|
|
146
|
+
return await signUpWithEmail(email, password, displayName);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
const error2 = err;
|
|
149
|
+
setError(error2);
|
|
150
|
+
throw error2;
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
[]
|
|
154
|
+
);
|
|
155
|
+
const signOut2 = react.useCallback(async () => {
|
|
156
|
+
setError(null);
|
|
157
|
+
try {
|
|
158
|
+
await signOut();
|
|
159
|
+
} catch (err) {
|
|
160
|
+
const error2 = err;
|
|
161
|
+
setError(error2);
|
|
162
|
+
throw error2;
|
|
163
|
+
}
|
|
164
|
+
}, []);
|
|
165
|
+
const signInWithGoogle2 = react.useCallback(async () => {
|
|
166
|
+
setError(null);
|
|
167
|
+
try {
|
|
168
|
+
return await signInWithGoogle();
|
|
169
|
+
} catch (err) {
|
|
170
|
+
const error2 = err;
|
|
171
|
+
setError(error2);
|
|
172
|
+
throw error2;
|
|
173
|
+
}
|
|
174
|
+
}, []);
|
|
175
|
+
const resetPassword2 = react.useCallback(async (email) => {
|
|
176
|
+
setError(null);
|
|
177
|
+
try {
|
|
178
|
+
await resetPassword(email);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
const error2 = err;
|
|
181
|
+
setError(error2);
|
|
182
|
+
throw error2;
|
|
183
|
+
}
|
|
184
|
+
}, []);
|
|
185
|
+
const updateProfile2 = react.useCallback(
|
|
186
|
+
async (profile) => {
|
|
187
|
+
setError(null);
|
|
188
|
+
if (!user) {
|
|
189
|
+
const error2 = new Error("No authenticated user");
|
|
190
|
+
setError(error2);
|
|
191
|
+
throw error2;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
await updateUserProfile(user, profile);
|
|
195
|
+
} catch (err) {
|
|
196
|
+
const error2 = err;
|
|
197
|
+
setError(error2);
|
|
198
|
+
throw error2;
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
[user]
|
|
202
|
+
);
|
|
203
|
+
const sendEmailVerificationFn = react.useCallback(async () => {
|
|
204
|
+
setError(null);
|
|
205
|
+
if (!user) {
|
|
206
|
+
const error2 = new Error("No authenticated user");
|
|
207
|
+
setError(error2);
|
|
208
|
+
throw error2;
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
await sendVerificationEmail(user);
|
|
212
|
+
} catch (err) {
|
|
213
|
+
const error2 = err;
|
|
214
|
+
setError(error2);
|
|
215
|
+
throw error2;
|
|
216
|
+
}
|
|
217
|
+
}, [user]);
|
|
218
|
+
const clearError = react.useCallback(() => {
|
|
219
|
+
setError(null);
|
|
220
|
+
}, []);
|
|
221
|
+
const value = react.useMemo(
|
|
222
|
+
() => ({
|
|
223
|
+
user,
|
|
224
|
+
loading,
|
|
225
|
+
isAuthenticated: !!user,
|
|
226
|
+
error,
|
|
227
|
+
signIn,
|
|
228
|
+
signUp,
|
|
229
|
+
signOut: signOut2,
|
|
230
|
+
signInWithGoogle: signInWithGoogle2,
|
|
231
|
+
resetPassword: resetPassword2,
|
|
232
|
+
updateProfile: updateProfile2,
|
|
233
|
+
sendEmailVerification: sendEmailVerificationFn,
|
|
234
|
+
clearError
|
|
235
|
+
}),
|
|
236
|
+
[
|
|
237
|
+
user,
|
|
238
|
+
loading,
|
|
239
|
+
error,
|
|
240
|
+
signIn,
|
|
241
|
+
signUp,
|
|
242
|
+
signOut2,
|
|
243
|
+
signInWithGoogle2,
|
|
244
|
+
resetPassword2,
|
|
245
|
+
updateProfile2,
|
|
246
|
+
sendEmailVerificationFn,
|
|
247
|
+
clearError
|
|
248
|
+
]
|
|
249
|
+
);
|
|
250
|
+
if (loading && loadingComponent) {
|
|
251
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent });
|
|
252
|
+
}
|
|
253
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AuthContext.Provider, { value, children });
|
|
254
|
+
}
|
|
255
|
+
function useAuthContext() {
|
|
256
|
+
const context = react.useContext(AuthContext);
|
|
257
|
+
if (context === void 0) {
|
|
258
|
+
throw new Error("useAuthContext must be used within an AuthProvider");
|
|
259
|
+
}
|
|
260
|
+
return context;
|
|
261
|
+
}
|
|
262
|
+
function withAuth(WrappedComponent, options = {}) {
|
|
263
|
+
const { LoadingComponent, UnauthenticatedComponent } = options;
|
|
264
|
+
return function WithAuthComponent(props) {
|
|
265
|
+
const { user, loading, isAuthenticated } = useAuthContext();
|
|
266
|
+
if (loading) {
|
|
267
|
+
return LoadingComponent ? /* @__PURE__ */ jsxRuntime.jsx(LoadingComponent, {}) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Loading..." });
|
|
268
|
+
}
|
|
269
|
+
if (!isAuthenticated) {
|
|
270
|
+
return UnauthenticatedComponent ? /* @__PURE__ */ jsxRuntime.jsx(UnauthenticatedComponent, {}) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Please sign in to continue" });
|
|
271
|
+
}
|
|
272
|
+
return /* @__PURE__ */ jsxRuntime.jsx(WrappedComponent, { ...props });
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
exports.AuthProvider = AuthProvider;
|
|
277
|
+
exports.useAuthContext = useAuthContext;
|
|
278
|
+
exports.withAuth = withAuth;
|
|
279
|
+
//# sourceMappingURL=index.js.map
|
|
280
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/firebase/config.ts","../../src/firebase/auth.ts","../../src/context/AuthContext.tsx"],"names":["getApps","getApp","initializeApp","getAuth","getFirestore","getStorage","auth","signInWithEmailAndPassword","createUserWithEmailAndPassword","updateProfile","firebaseSignOut","sendPasswordResetEmail","sendEmailVerification","GoogleAuthProvider","signInWithPopup","onAuthStateChanged","createContext","useState","useEffect","user","useCallback","error","signOut","signInWithGoogle","resetPassword","useMemo","jsx","useContext"],"mappings":"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","file":"index.js","sourcesContent":["import { initializeApp, getApps, getApp, FirebaseApp } from 'firebase/app';\nimport { getAuth, Auth } from 'firebase/auth';\nimport { getFirestore, Firestore } from 'firebase/firestore';\nimport { getStorage, FirebaseStorage } from 'firebase/storage';\n\n/**\n * Firebase configuration interface\n * All values should be provided via environment variables\n */\nexport interface FirebaseConfig {\n apiKey: string;\n authDomain: string;\n projectId: string;\n storageBucket: string;\n messagingSenderId: string;\n appId: string;\n measurementId?: string;\n}\n\n/**\n * Get Firebase configuration from environment variables\n * Works with Next.js NEXT_PUBLIC_ prefix\n */\nexport function getFirebaseConfig(): FirebaseConfig {\n const config: FirebaseConfig = {\n apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || '',\n authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || '',\n projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || '',\n storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || '',\n messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || '',\n appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || '',\n measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,\n };\n\n // Validate required fields\n const requiredFields: (keyof FirebaseConfig)[] = [\n 'apiKey',\n 'authDomain',\n 'projectId',\n 'storageBucket',\n 'messagingSenderId',\n 'appId',\n ];\n\n const missingFields = requiredFields.filter((field) => !config[field]);\n\n if (missingFields.length > 0) {\n console.warn(\n `[mw-core] Missing Firebase config fields: ${missingFields.join(', ')}. ` +\n `Make sure to set NEXT_PUBLIC_FIREBASE_* environment variables.`\n );\n }\n\n return config;\n}\n\n/**\n * Initialize Firebase app (singleton pattern)\n * Safe to call multiple times - returns existing instance if already initialized\n */\nexport function initializeFirebase(config?: FirebaseConfig): FirebaseApp {\n if (getApps().length > 0) {\n return getApp();\n }\n\n const firebaseConfig = config || getFirebaseConfig();\n return initializeApp(firebaseConfig);\n}\n\n// Lazy initialization singletons\nlet _app: FirebaseApp | null = null;\nlet _auth: Auth | null = null;\nlet _db: Firestore | null = null;\nlet _storage: FirebaseStorage | null = null;\n\n/**\n * Get the Firebase app instance\n */\nexport function getFirebaseApp(): FirebaseApp {\n if (!_app) {\n _app = initializeFirebase();\n }\n return _app;\n}\n\n/**\n * Get the Firebase Auth instance\n */\nexport function getFirebaseAuth(): Auth {\n if (!_auth) {\n _auth = getAuth(getFirebaseApp());\n }\n return _auth;\n}\n\n/**\n * Get the Firestore database instance\n */\nexport function getFirebaseFirestore(): Firestore {\n if (!_db) {\n _db = getFirestore(getFirebaseApp());\n }\n return _db;\n}\n\n/**\n * Get the Firebase Storage instance\n */\nexport function getFirebaseStorage(): FirebaseStorage {\n if (!_storage) {\n _storage = getStorage(getFirebaseApp());\n }\n return _storage;\n}\n\n// Convenience exports for direct access\nexport const app = typeof window !== 'undefined' ? getFirebaseApp() : null;\nexport const auth = typeof window !== 'undefined' ? getFirebaseAuth() : null;\nexport const db = typeof window !== 'undefined' ? getFirebaseFirestore() : null;\nexport const storage = typeof window !== 'undefined' ? getFirebaseStorage() : null;\n\n","import {\n signInWithEmailAndPassword,\n createUserWithEmailAndPassword,\n signOut as firebaseSignOut,\n sendPasswordResetEmail,\n sendEmailVerification,\n updateProfile,\n GoogleAuthProvider,\n signInWithPopup,\n signInWithRedirect,\n getRedirectResult,\n onAuthStateChanged,\n User,\n UserCredential,\n Auth,\n} from 'firebase/auth';\nimport { getFirebaseAuth } from './config';\n\n/**\n * Sign in with email and password\n */\nexport async function signInWithEmail(\n email: string,\n password: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n return signInWithEmailAndPassword(auth, email, password);\n}\n\n/**\n * Create a new account with email and password\n */\nexport async function signUpWithEmail(\n email: string,\n password: string,\n displayName?: string\n): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const credential = await createUserWithEmailAndPassword(auth, email, password);\n \n if (displayName && credential.user) {\n await updateProfile(credential.user, { displayName });\n }\n \n return credential;\n}\n\n/**\n * Sign out the current user\n */\nexport async function signOut(): Promise<void> {\n const auth = getFirebaseAuth();\n return firebaseSignOut(auth);\n}\n\n/**\n * Send a password reset email\n */\nexport async function resetPassword(email: string): Promise<void> {\n const auth = getFirebaseAuth();\n return sendPasswordResetEmail(auth, email);\n}\n\n/**\n * Send email verification to the current user\n */\nexport async function sendVerificationEmail(user: User): Promise<void> {\n return sendEmailVerification(user);\n}\n\n/**\n * Update the current user's profile\n */\nexport async function updateUserProfile(\n user: User,\n profile: { displayName?: string; photoURL?: string }\n): Promise<void> {\n return updateProfile(user, profile);\n}\n\n/**\n * Sign in with Google using popup\n */\nexport async function signInWithGoogle(): Promise<UserCredential> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithPopup(auth, provider);\n}\n\n/**\n * Sign in with Google using redirect (better for mobile)\n */\nexport async function signInWithGoogleRedirect(): Promise<void> {\n const auth = getFirebaseAuth();\n const provider = new GoogleAuthProvider();\n provider.addScope('email');\n provider.addScope('profile');\n return signInWithRedirect(auth, provider);\n}\n\n/**\n * Get the result of a redirect sign-in\n */\nexport async function getGoogleRedirectResult(): Promise<UserCredential | null> {\n const auth = getFirebaseAuth();\n return getRedirectResult(auth);\n}\n\n/**\n * Subscribe to auth state changes\n */\nexport function subscribeToAuthState(\n callback: (user: User | null) => void\n): () => void {\n const auth = getFirebaseAuth();\n return onAuthStateChanged(auth, callback);\n}\n\n/**\n * Get the current user (may be null if not authenticated)\n */\nexport function getCurrentUser(): User | null {\n const auth = getFirebaseAuth();\n return auth.currentUser;\n}\n\n/**\n * Wait for the auth state to be determined\n * Useful for SSR/initial load\n */\nexport function waitForAuthState(): Promise<User | null> {\n return new Promise((resolve) => {\n const auth = getFirebaseAuth();\n const unsubscribe = onAuthStateChanged(auth, (user) => {\n unsubscribe();\n resolve(user);\n });\n });\n}\n\n// Re-export useful types\nexport type { User, UserCredential, Auth };\n\n","'use client';\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useCallback,\n useMemo,\n ReactNode,\n} from 'react';\nimport { User, UserCredential } from 'firebase/auth';\nimport {\n subscribeToAuthState,\n signInWithEmail,\n signUpWithEmail,\n signOut as firebaseSignOut,\n signInWithGoogle as firebaseSignInWithGoogle,\n resetPassword as firebaseResetPassword,\n updateUserProfile,\n sendVerificationEmail,\n} from '../firebase/auth';\n\nexport interface AuthContextValue {\n /** The current authenticated user, or null if not authenticated */\n user: User | null;\n /** Whether the auth state is still loading */\n loading: boolean;\n /** Whether the user is authenticated */\n isAuthenticated: boolean;\n /** Any auth error that occurred */\n error: Error | null;\n /** Sign in with email and password */\n signIn: (email: string, password: string) => Promise<UserCredential>;\n /** Create a new account with email and password */\n signUp: (email: string, password: string, displayName?: string) => Promise<UserCredential>;\n /** Sign out the current user */\n signOut: () => Promise<void>;\n /** Sign in with Google */\n signInWithGoogle: () => Promise<UserCredential>;\n /** Send a password reset email */\n resetPassword: (email: string) => Promise<void>;\n /** Update the user's profile */\n updateProfile: (profile: { displayName?: string; photoURL?: string }) => Promise<void>;\n /** Send email verification */\n sendEmailVerification: () => Promise<void>;\n /** Clear any auth errors */\n clearError: () => void;\n}\n\nconst AuthContext = createContext<AuthContextValue | undefined>(undefined);\n\nexport interface AuthProviderProps {\n children: ReactNode;\n /** Optional: Called when auth state changes */\n onAuthStateChange?: (user: User | null) => void;\n /** Optional: Loading component to show while auth state is loading */\n loadingComponent?: ReactNode;\n}\n\n/**\n * Auth Provider component that wraps your app and provides auth context\n * \n * @example\n * ```tsx\n * // app/layout.tsx\n * import { AuthProvider } from '@marcwelti/mw-core';\n * \n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <AuthProvider>\n * {children}\n * </AuthProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function AuthProvider({\n children,\n onAuthStateChange,\n loadingComponent,\n}: AuthProviderProps) {\n const [user, setUser] = useState<User | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n const unsubscribe = subscribeToAuthState((user) => {\n setUser(user);\n setLoading(false);\n onAuthStateChange?.(user);\n });\n\n return unsubscribe;\n }, [onAuthStateChange]);\n\n const signIn = useCallback(async (email: string, password: string) => {\n setError(null);\n try {\n return await signInWithEmail(email, password);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signUp = useCallback(\n async (email: string, password: string, displayName?: string) => {\n setError(null);\n try {\n return await signUpWithEmail(email, password, displayName);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n []\n );\n\n const signOut = useCallback(async () => {\n setError(null);\n try {\n await firebaseSignOut();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const signInWithGoogle = useCallback(async () => {\n setError(null);\n try {\n return await firebaseSignInWithGoogle();\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const resetPassword = useCallback(async (email: string) => {\n setError(null);\n try {\n await firebaseResetPassword(email);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, []);\n\n const updateProfile = useCallback(\n async (profile: { displayName?: string; photoURL?: string }) => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await updateUserProfile(user, profile);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n },\n [user]\n );\n\n const sendEmailVerificationFn = useCallback(async () => {\n setError(null);\n if (!user) {\n const error = new Error('No authenticated user');\n setError(error);\n throw error;\n }\n try {\n await sendVerificationEmail(user);\n } catch (err) {\n const error = err as Error;\n setError(error);\n throw error;\n }\n }, [user]);\n\n const clearError = useCallback(() => {\n setError(null);\n }, []);\n\n const value = useMemo<AuthContextValue>(\n () => ({\n user,\n loading,\n isAuthenticated: !!user,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerification: sendEmailVerificationFn,\n clearError,\n }),\n [\n user,\n loading,\n error,\n signIn,\n signUp,\n signOut,\n signInWithGoogle,\n resetPassword,\n updateProfile,\n sendEmailVerificationFn,\n clearError,\n ]\n );\n\n // Show loading component while determining auth state\n if (loading && loadingComponent) {\n return <>{loadingComponent}</>;\n }\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Hook to access auth context\n * Must be used within an AuthProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, signOut, isAuthenticated } = useAuthContext();\n * \n * if (!isAuthenticated) {\n * return <LoginForm />;\n * }\n * \n * return (\n * <div>\n * <p>Welcome, {user?.displayName}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useAuthContext(): AuthContextValue {\n const context = useContext(AuthContext);\n \n if (context === undefined) {\n throw new Error('useAuthContext must be used within an AuthProvider');\n }\n \n return context;\n}\n\n/**\n * HOC for protecting routes that require authentication\n * Redirects to login or shows loading state\n */\nexport function withAuth<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n options: {\n LoadingComponent?: React.ComponentType;\n UnauthenticatedComponent?: React.ComponentType;\n } = {}\n) {\n const { LoadingComponent, UnauthenticatedComponent } = options;\n\n return function WithAuthComponent(props: P) {\n const { user, loading, isAuthenticated } = useAuthContext();\n\n if (loading) {\n return LoadingComponent ? <LoadingComponent /> : <div>Loading...</div>;\n }\n\n if (!isAuthenticated) {\n return UnauthenticatedComponent ? (\n <UnauthenticatedComponent />\n ) : (\n <div>Please sign in to continue</div>\n );\n }\n\n return <WrappedComponent {...props} />;\n };\n}\n\n"]}
|