hazo_auth 4.0.0 → 4.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 +312 -8
- package/SETUP_CHECKLIST.md +203 -0
- package/dist/app/api/hazo_auth/forgot_password/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/forgot_password/route.js +15 -0
- package/dist/app/api/hazo_auth/logout/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/logout/route.js +31 -0
- package/dist/app/api/hazo_auth/me/route.d.ts +3 -0
- package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/me/route.js +19 -1
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts +2 -0
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.js +8 -0
- package/dist/components/layouts/forgot_password/index.d.ts +7 -1
- package/dist/components/layouts/forgot_password/index.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/index.js +7 -2
- package/dist/components/layouts/login/index.d.ts +13 -1
- package/dist/components/layouts/login/index.d.ts.map +1 -1
- package/dist/components/layouts/login/index.js +11 -2
- package/dist/components/layouts/my_settings/components/connected_accounts_section.d.ts +17 -0
- package/dist/components/layouts/my_settings/components/connected_accounts_section.d.ts.map +1 -0
- package/dist/components/layouts/my_settings/components/connected_accounts_section.js +17 -0
- package/dist/components/layouts/my_settings/components/set_password_section.d.ts +26 -0
- package/dist/components/layouts/my_settings/components/set_password_section.d.ts.map +1 -0
- package/dist/components/layouts/my_settings/components/set_password_section.js +127 -0
- package/dist/components/layouts/my_settings/hooks/use_my_settings.d.ts +3 -0
- package/dist/components/layouts/my_settings/hooks/use_my_settings.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/hooks/use_my_settings.js +9 -0
- package/dist/components/layouts/my_settings/index.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/index.js +4 -2
- package/dist/components/layouts/profile_stamp_test/index.d.ts +10 -0
- package/dist/components/layouts/profile_stamp_test/index.d.ts.map +1 -0
- package/dist/components/layouts/profile_stamp_test/index.js +51 -0
- package/dist/components/layouts/shared/components/google_icon.d.ts +12 -0
- package/dist/components/layouts/shared/components/google_icon.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/google_icon.js +9 -0
- package/dist/components/layouts/shared/components/google_sign_in_button.d.ts +21 -0
- package/dist/components/layouts/shared/components/google_sign_in_button.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/google_sign_in_button.js +50 -0
- package/dist/components/layouts/shared/components/oauth_divider.d.ts +13 -0
- package/dist/components/layouts/shared/components/oauth_divider.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/oauth_divider.js +13 -0
- package/dist/components/layouts/shared/components/profile_stamp.d.ts +58 -0
- package/dist/components/layouts/shared/components/profile_stamp.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/profile_stamp.js +72 -0
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +2 -2
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts +6 -0
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts.map +1 -1
- package/dist/components/layouts/shared/hooks/use_auth_status.js +8 -0
- package/dist/components/layouts/shared/index.d.ts +7 -0
- package/dist/components/layouts/shared/index.d.ts.map +1 -1
- package/dist/components/layouts/shared/index.js +4 -0
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/hover-card.d.ts +7 -0
- package/dist/components/ui/hover-card.d.ts.map +1 -0
- package/dist/components/ui/hover-card.js +29 -0
- package/dist/components/ui/index.d.ts +1 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +1 -0
- package/dist/lib/auth/nextauth_config.d.ts +34 -0
- package/dist/lib/auth/nextauth_config.d.ts.map +1 -0
- package/dist/lib/auth/nextauth_config.js +171 -0
- package/dist/lib/config/default_config.d.ts +24 -0
- package/dist/lib/config/default_config.d.ts.map +1 -1
- package/dist/lib/config/default_config.js +14 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/lib/login_config.server.d.ts +3 -0
- package/dist/lib/login_config.server.d.ts.map +1 -1
- package/dist/lib/login_config.server.js +4 -0
- package/dist/lib/oauth_config.server.d.ts +29 -0
- package/dist/lib/oauth_config.server.d.ts.map +1 -0
- package/dist/lib/oauth_config.server.js +40 -0
- package/dist/lib/services/login_service.d.ts.map +1 -1
- package/dist/lib/services/login_service.js +16 -1
- package/dist/lib/services/oauth_service.d.ts +88 -0
- package/dist/lib/services/oauth_service.d.ts.map +1 -0
- package/dist/lib/services/oauth_service.js +376 -0
- package/dist/lib/services/password_reset_service.d.ts +2 -0
- package/dist/lib/services/password_reset_service.d.ts.map +1 -1
- package/dist/lib/services/password_reset_service.js +10 -0
- package/dist/lib/services/registration_service.d.ts.map +1 -1
- package/dist/lib/services/registration_service.js +1 -0
- package/dist/lib/utils/password_validator.d.ts +13 -0
- package/dist/lib/utils/password_validator.d.ts.map +1 -0
- package/dist/lib/utils/password_validator.js +36 -0
- package/dist/server_pages/login.d.ts.map +1 -1
- package/dist/server_pages/login.js +6 -1
- package/dist/server_pages/login_client_wrapper.d.ts +5 -2
- package/dist/server_pages/login_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/login_client_wrapper.js +2 -2
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ A reusable authentication UI component package powered by Next.js, TailwindCSS,
|
|
|
24
24
|
- [Quick Start](#quick-start)
|
|
25
25
|
- [Configuration Setup](#configuration-setup)
|
|
26
26
|
- [Database Setup](#database-setup)
|
|
27
|
+
- [Google OAuth Setup](#google-oauth-setup)
|
|
27
28
|
- [Using Components](#using-components)
|
|
28
29
|
- [Authentication Service](#authentication-service)
|
|
29
30
|
- [Proxy/Middleware Authentication](#proxymiddleware-authentication)
|
|
@@ -198,11 +199,12 @@ import { MySettingsLayout } from "hazo_auth/components/layouts/my_settings";
|
|
|
198
199
|
import { UserManagementLayout } from "hazo_auth/components/layouts/user_management";
|
|
199
200
|
|
|
200
201
|
// Import shared components and hooks from barrel export
|
|
201
|
-
import {
|
|
202
|
-
ProfilePicMenu,
|
|
202
|
+
import {
|
|
203
|
+
ProfilePicMenu,
|
|
203
204
|
ProfilePicMenuWrapper,
|
|
204
|
-
|
|
205
|
-
|
|
205
|
+
ProfileStamp,
|
|
206
|
+
use_hazo_auth,
|
|
207
|
+
use_auth_status
|
|
206
208
|
} from "hazo_auth/components/layouts/shared";
|
|
207
209
|
|
|
208
210
|
// Import server-side utilities
|
|
@@ -258,11 +260,12 @@ For client components (browser-safe, no Node.js dependencies):
|
|
|
258
260
|
|
|
259
261
|
```typescript
|
|
260
262
|
// Use hazo_auth/client for client components
|
|
261
|
-
import {
|
|
262
|
-
ProfilePicMenu,
|
|
263
|
-
|
|
263
|
+
import {
|
|
264
|
+
ProfilePicMenu,
|
|
265
|
+
ProfileStamp,
|
|
266
|
+
use_auth_status,
|
|
264
267
|
use_hazo_auth,
|
|
265
|
-
cn
|
|
268
|
+
cn
|
|
266
269
|
} from "hazo_auth/client";
|
|
267
270
|
```
|
|
268
271
|
|
|
@@ -364,6 +367,19 @@ cp node_modules/hazo_auth/hazo_notify_config.example.ini ./hazo_notify_config.in
|
|
|
364
367
|
|
|
365
368
|
**Note:** `JWT_SECRET` is required for JWT session token functionality (used for Edge-compatible proxy/middleware authentication). Generate a secure random string at least 32 characters long.
|
|
366
369
|
|
|
370
|
+
**For Google OAuth (optional):**
|
|
371
|
+
```env
|
|
372
|
+
# NextAuth.js configuration (required for OAuth)
|
|
373
|
+
NEXTAUTH_SECRET=your_secure_random_string_at_least_32_characters
|
|
374
|
+
NEXTAUTH_URL=http://localhost:3000 # Change to production URL in production
|
|
375
|
+
|
|
376
|
+
# Google OAuth credentials (from Google Cloud Console)
|
|
377
|
+
HAZO_AUTH_GOOGLE_CLIENT_ID=your_google_client_id
|
|
378
|
+
HAZO_AUTH_GOOGLE_CLIENT_SECRET=your_google_client_secret
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
See [Google OAuth Setup](#google-oauth-setup) for detailed instructions.
|
|
382
|
+
|
|
367
383
|
**Important:** The configuration files must be located in your project root directory (where `process.cwd()` points to), not inside `node_modules`. The package reads configuration from `process.cwd()` at runtime, so storing them elsewhere (including `node_modules/hazo_auth`) will break runtime access.
|
|
368
384
|
|
|
369
385
|
---
|
|
@@ -670,6 +686,222 @@ npx tsx scripts/apply_migration.ts
|
|
|
670
686
|
|
|
671
687
|
---
|
|
672
688
|
|
|
689
|
+
## Google OAuth Setup
|
|
690
|
+
|
|
691
|
+
hazo_auth supports Google Sign-In via NextAuth.js v4, allowing users to authenticate with their Google accounts.
|
|
692
|
+
|
|
693
|
+
### Features
|
|
694
|
+
|
|
695
|
+
- **Dual authentication**: Users can have BOTH Google OAuth and email/password login
|
|
696
|
+
- **Auto-linking**: Automatically links Google login to existing unverified email/password accounts
|
|
697
|
+
- **Graceful degradation**: Login page adapts based on enabled authentication methods
|
|
698
|
+
- **Set password feature**: Google-only users can add a password later via My Settings
|
|
699
|
+
- **Profile data**: Full name and profile picture automatically populated from Google
|
|
700
|
+
|
|
701
|
+
### Step 1: Get Google OAuth Credentials
|
|
702
|
+
|
|
703
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
704
|
+
2. Create a project or select an existing project
|
|
705
|
+
3. Enable Google+ API (or Google Identity Services)
|
|
706
|
+
4. Navigate to **Credentials** → **Create Credentials** → **OAuth 2.0 Client ID**
|
|
707
|
+
5. Configure OAuth consent screen if prompted
|
|
708
|
+
6. Set **Application type** to "Web application"
|
|
709
|
+
7. Add **Authorized JavaScript origins**:
|
|
710
|
+
- Development: `http://localhost:3000`
|
|
711
|
+
- Production: `https://yourdomain.com`
|
|
712
|
+
8. Add **Authorized redirect URIs**:
|
|
713
|
+
- Development: `http://localhost:3000/api/auth/callback/google`
|
|
714
|
+
- Production: `https://yourdomain.com/api/auth/callback/google`
|
|
715
|
+
9. Copy the **Client ID** and **Client Secret**
|
|
716
|
+
|
|
717
|
+
### Step 2: Add Environment Variables
|
|
718
|
+
|
|
719
|
+
Add to your `.env.local`:
|
|
720
|
+
|
|
721
|
+
```env
|
|
722
|
+
# NextAuth.js configuration (REQUIRED for OAuth)
|
|
723
|
+
NEXTAUTH_SECRET=your_secure_random_string_at_least_32_characters
|
|
724
|
+
NEXTAUTH_URL=http://localhost:3000 # Change to production URL in production
|
|
725
|
+
|
|
726
|
+
# Google OAuth credentials (from Google Cloud Console)
|
|
727
|
+
HAZO_AUTH_GOOGLE_CLIENT_ID=your_google_client_id_from_step_1
|
|
728
|
+
HAZO_AUTH_GOOGLE_CLIENT_SECRET=your_google_client_secret_from_step_1
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**Generate NEXTAUTH_SECRET:**
|
|
732
|
+
```bash
|
|
733
|
+
openssl rand -base64 32
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### Step 3: Run Database Migration
|
|
737
|
+
|
|
738
|
+
Add OAuth fields to the `hazo_users` table:
|
|
739
|
+
|
|
740
|
+
```bash
|
|
741
|
+
npm run migrate migrations/005_add_oauth_fields_to_hazo_users.sql
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
This migration adds:
|
|
745
|
+
- `google_id` - Google's unique user ID (TEXT, UNIQUE)
|
|
746
|
+
- `auth_providers` - Tracks authentication methods: 'email', 'google', or 'email,google'
|
|
747
|
+
- Index on `google_id` for fast OAuth lookups
|
|
748
|
+
|
|
749
|
+
**Manual migration (if needed):**
|
|
750
|
+
|
|
751
|
+
**PostgreSQL:**
|
|
752
|
+
```sql
|
|
753
|
+
ALTER TABLE hazo_users
|
|
754
|
+
ADD COLUMN google_id TEXT UNIQUE;
|
|
755
|
+
|
|
756
|
+
ALTER TABLE hazo_users
|
|
757
|
+
ADD COLUMN auth_providers TEXT DEFAULT 'email';
|
|
758
|
+
|
|
759
|
+
CREATE INDEX IF NOT EXISTS idx_hazo_users_google_id ON hazo_users(google_id);
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
**SQLite:**
|
|
763
|
+
```sql
|
|
764
|
+
ALTER TABLE hazo_users
|
|
765
|
+
ADD COLUMN google_id TEXT;
|
|
766
|
+
|
|
767
|
+
ALTER TABLE hazo_users
|
|
768
|
+
ADD COLUMN auth_providers TEXT DEFAULT 'email';
|
|
769
|
+
|
|
770
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_hazo_users_google_id_unique ON hazo_users(google_id);
|
|
771
|
+
CREATE INDEX IF NOT EXISTS idx_hazo_users_google_id ON hazo_users(google_id);
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
### Step 4: Configure OAuth in hazo_auth_config.ini
|
|
775
|
+
|
|
776
|
+
```ini
|
|
777
|
+
[hazo_auth__oauth]
|
|
778
|
+
# Enable Google OAuth login (default: true)
|
|
779
|
+
enable_google = true
|
|
780
|
+
|
|
781
|
+
# Enable traditional email/password login (default: true)
|
|
782
|
+
enable_email_password = true
|
|
783
|
+
|
|
784
|
+
# Auto-link Google login to existing unverified email/password accounts (default: true)
|
|
785
|
+
auto_link_unverified_accounts = true
|
|
786
|
+
|
|
787
|
+
# Customize button text (optional)
|
|
788
|
+
google_button_text = Continue with Google
|
|
789
|
+
oauth_divider_text = or
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
### Step 5: Create NextAuth API Routes
|
|
793
|
+
|
|
794
|
+
Create `app/api/auth/[...nextauth]/route.ts`:
|
|
795
|
+
|
|
796
|
+
```typescript
|
|
797
|
+
export { GET, POST } from "hazo_auth/server/routes/nextauth";
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
Create `app/api/hazo_auth/oauth/google/callback/route.ts`:
|
|
801
|
+
|
|
802
|
+
```typescript
|
|
803
|
+
export { GET } from "hazo_auth/server/routes/oauth_google_callback";
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
Create `app/api/hazo_auth/set_password/route.ts`:
|
|
807
|
+
|
|
808
|
+
```typescript
|
|
809
|
+
export { POST } from "hazo_auth/server/routes/set_password";
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
**Or use the CLI generator:**
|
|
813
|
+
```bash
|
|
814
|
+
npx hazo_auth generate-routes --oauth
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
### Step 6: Test Google OAuth
|
|
818
|
+
|
|
819
|
+
1. Start your dev server: `npm run dev`
|
|
820
|
+
2. Visit `http://localhost:3000/hazo_auth/login`
|
|
821
|
+
3. You should see the "Sign in with Google" button
|
|
822
|
+
4. Click it and authenticate with your Google account
|
|
823
|
+
5. You'll be redirected back and logged in
|
|
824
|
+
|
|
825
|
+
### User Flows
|
|
826
|
+
|
|
827
|
+
**New User - Google Sign-In:**
|
|
828
|
+
- User clicks "Sign in with Google"
|
|
829
|
+
- Authenticates with Google
|
|
830
|
+
- Account created with Google profile data (email, name, profile picture)
|
|
831
|
+
- Email is automatically verified
|
|
832
|
+
- User can log in with Google anytime
|
|
833
|
+
|
|
834
|
+
**Existing Unverified User - Google Sign-In:**
|
|
835
|
+
- User has email/password account but hasn't verified email
|
|
836
|
+
- Clicks "Sign in with Google" with same email
|
|
837
|
+
- System auto-links Google account (if `auto_link_unverified_accounts = true`)
|
|
838
|
+
- Email becomes verified
|
|
839
|
+
- User can now log in with EITHER Google OR email/password
|
|
840
|
+
|
|
841
|
+
**Google-Only User Adds Password:**
|
|
842
|
+
- Google-only user visits My Settings
|
|
843
|
+
- "Set Password" section appears
|
|
844
|
+
- User sets a password
|
|
845
|
+
- User can now log in with EITHER method
|
|
846
|
+
|
|
847
|
+
**Google-Only User Tries Forgot Password:**
|
|
848
|
+
- User registered with Google tries "Forgot Password"
|
|
849
|
+
- System shows: "You registered with Google. Please sign in with Google instead."
|
|
850
|
+
|
|
851
|
+
### Configuration Options
|
|
852
|
+
|
|
853
|
+
**Disable email/password login (Google-only):**
|
|
854
|
+
```ini
|
|
855
|
+
[hazo_auth__oauth]
|
|
856
|
+
enable_google = true
|
|
857
|
+
enable_email_password = false
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
**Disable Google OAuth (email/password only):**
|
|
861
|
+
```ini
|
|
862
|
+
[hazo_auth__oauth]
|
|
863
|
+
enable_google = false
|
|
864
|
+
enable_email_password = true
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
### API Response Changes
|
|
868
|
+
|
|
869
|
+
The `/api/hazo_auth/me` endpoint now includes OAuth status:
|
|
870
|
+
|
|
871
|
+
```typescript
|
|
872
|
+
{
|
|
873
|
+
authenticated: true,
|
|
874
|
+
// ... existing fields
|
|
875
|
+
auth_providers: "email,google", // NEW: Tracks authentication methods
|
|
876
|
+
has_password: true, // NEW: Whether user has password set
|
|
877
|
+
google_connected: true, // NEW: Whether Google account is linked
|
|
878
|
+
}
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
### Dependencies
|
|
882
|
+
|
|
883
|
+
Google OAuth adds one new dependency:
|
|
884
|
+
- `next-auth@^4.24.11` - NextAuth.js for OAuth handling (automatically installed with hazo_auth)
|
|
885
|
+
|
|
886
|
+
### Troubleshooting
|
|
887
|
+
|
|
888
|
+
**"Sign in with Google" button not showing:**
|
|
889
|
+
- Verify `enable_google = true` in `[hazo_auth__oauth]` section
|
|
890
|
+
- Check `HAZO_AUTH_GOOGLE_CLIENT_ID` and `HAZO_AUTH_GOOGLE_CLIENT_SECRET` are set
|
|
891
|
+
- Check `NEXTAUTH_URL` matches your current URL
|
|
892
|
+
|
|
893
|
+
**OAuth callback error:**
|
|
894
|
+
- Verify redirect URI in Google Cloud Console matches exactly: `http://localhost:3000/api/auth/callback/google`
|
|
895
|
+
- Check `NEXTAUTH_SECRET` is set and at least 32 characters
|
|
896
|
+
- Verify API routes are created: `/api/auth/[...nextauth]/route.ts` and `/api/hazo_auth/oauth/google/callback/route.ts`
|
|
897
|
+
|
|
898
|
+
**User created but not logged in:**
|
|
899
|
+
- Check browser console for errors
|
|
900
|
+
- Verify `/api/hazo_auth/oauth/google/callback` route exists
|
|
901
|
+
- Check server logs for errors during session creation
|
|
902
|
+
|
|
903
|
+
---
|
|
904
|
+
|
|
673
905
|
## Using Components
|
|
674
906
|
|
|
675
907
|
### Package Exports
|
|
@@ -863,6 +1095,10 @@ This is the **standardized endpoint** that ensures consistent response format ac
|
|
|
863
1095
|
last_logon: string | undefined,
|
|
864
1096
|
profile_picture_url: string | null,
|
|
865
1097
|
profile_source: "upload" | "library" | "gravatar" | "custom" | undefined,
|
|
1098
|
+
// Profile picture aliases (for consuming app compatibility)
|
|
1099
|
+
profile_image?: string, // Alias for profile_picture_url
|
|
1100
|
+
avatar_url?: string, // Alias for profile_picture_url
|
|
1101
|
+
image?: string, // Alias for profile_picture_url
|
|
866
1102
|
// Permissions (always included)
|
|
867
1103
|
user: {
|
|
868
1104
|
id: string,
|
|
@@ -1375,6 +1611,74 @@ The test tool uses the `/api/hazo_auth/rbac_test` endpoint which is included in
|
|
|
1375
1611
|
|
|
1376
1612
|
---
|
|
1377
1613
|
|
|
1614
|
+
## ProfileStamp Component
|
|
1615
|
+
|
|
1616
|
+
The `ProfileStamp` component is a drop-in widget that displays a circular profile picture with a hover card showing user details. Perfect for adding profile attribution to notes, comments, or any user-generated content.
|
|
1617
|
+
|
|
1618
|
+
### Features
|
|
1619
|
+
|
|
1620
|
+
- Displays user's profile picture or initials
|
|
1621
|
+
- Hover card with user name, email, and custom fields
|
|
1622
|
+
- Three sizes: sm (24px), default (32px), lg (40px)
|
|
1623
|
+
- Automatic loading state and unauthenticated fallback
|
|
1624
|
+
- Fully accessible with keyboard navigation
|
|
1625
|
+
|
|
1626
|
+
### Usage
|
|
1627
|
+
|
|
1628
|
+
```typescript
|
|
1629
|
+
import { ProfileStamp } from "hazo_auth/client";
|
|
1630
|
+
|
|
1631
|
+
// Basic usage
|
|
1632
|
+
<ProfileStamp />
|
|
1633
|
+
|
|
1634
|
+
// With custom size and fields
|
|
1635
|
+
<ProfileStamp
|
|
1636
|
+
size="lg"
|
|
1637
|
+
custom_fields={[
|
|
1638
|
+
{ label: "Role", value: "Admin" },
|
|
1639
|
+
{ label: "Department", value: "Engineering" }
|
|
1640
|
+
]}
|
|
1641
|
+
/>
|
|
1642
|
+
|
|
1643
|
+
// Hide default fields, only show custom fields
|
|
1644
|
+
<ProfileStamp
|
|
1645
|
+
show_name={false}
|
|
1646
|
+
show_email={false}
|
|
1647
|
+
custom_fields={[
|
|
1648
|
+
{ label: "Posted", value: "2 hours ago" }
|
|
1649
|
+
]}
|
|
1650
|
+
/>
|
|
1651
|
+
```
|
|
1652
|
+
|
|
1653
|
+
### Props
|
|
1654
|
+
|
|
1655
|
+
| Prop | Type | Default | Description |
|
|
1656
|
+
|------|------|---------|-------------|
|
|
1657
|
+
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Avatar size (sm: 24px, default: 32px, lg: 40px) |
|
|
1658
|
+
| `custom_fields` | `ProfileStampCustomField[]` | `[]` | Custom fields to display in hover card |
|
|
1659
|
+
| `className` | `string` | `undefined` | Additional CSS classes |
|
|
1660
|
+
| `show_name` | `boolean` | `true` | Show user name in hover card |
|
|
1661
|
+
| `show_email` | `boolean` | `true` | Show email in hover card |
|
|
1662
|
+
|
|
1663
|
+
### ProfileStampCustomField Type
|
|
1664
|
+
|
|
1665
|
+
```typescript
|
|
1666
|
+
type ProfileStampCustomField = {
|
|
1667
|
+
label: string; // Field label (e.g., "Role", "Department")
|
|
1668
|
+
value: string; // Field value (e.g., "Admin", "Engineering")
|
|
1669
|
+
};
|
|
1670
|
+
```
|
|
1671
|
+
|
|
1672
|
+
### Test Page
|
|
1673
|
+
|
|
1674
|
+
Visit `/hazo_auth/profile_stamp_test` in your dev environment to see examples of ProfileStamp with various configurations:
|
|
1675
|
+
- Size variants
|
|
1676
|
+
- Custom fields
|
|
1677
|
+
- Display options (showing/hiding name and email)
|
|
1678
|
+
- Usage scenarios (notes, comments, activity feeds)
|
|
1679
|
+
|
|
1680
|
+
---
|
|
1681
|
+
|
|
1378
1682
|
## Profile Picture Menu Widget
|
|
1379
1683
|
|
|
1380
1684
|
The Profile Picture Menu is a versatile component for navbar or sidebar that automatically displays:
|
package/SETUP_CHECKLIST.md
CHANGED
|
@@ -492,6 +492,169 @@ This script will:
|
|
|
492
492
|
|
|
493
493
|
---
|
|
494
494
|
|
|
495
|
+
## Phase 3.2: Google OAuth Setup (Optional)
|
|
496
|
+
|
|
497
|
+
Google OAuth Sign-In allows users to authenticate with their Google accounts. This section is optional - skip if you don't need OAuth.
|
|
498
|
+
|
|
499
|
+
### Step 3.2.1: Get Google OAuth Credentials
|
|
500
|
+
|
|
501
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
502
|
+
2. Create a project or select an existing project
|
|
503
|
+
3. Enable **Google+ API** (or Google Identity Services)
|
|
504
|
+
4. Navigate to **Credentials** → **Create Credentials** → **OAuth 2.0 Client ID**
|
|
505
|
+
5. Configure OAuth consent screen if prompted
|
|
506
|
+
6. Set **Application type** to "Web application"
|
|
507
|
+
7. Add **Authorized JavaScript origins**:
|
|
508
|
+
- Development: `http://localhost:3000`
|
|
509
|
+
- Production: `https://yourdomain.com`
|
|
510
|
+
8. Add **Authorized redirect URIs**:
|
|
511
|
+
- Development: `http://localhost:3000/api/auth/callback/google`
|
|
512
|
+
- Production: `https://yourdomain.com/api/auth/callback/google`
|
|
513
|
+
9. Copy the **Client ID** and **Client Secret**
|
|
514
|
+
|
|
515
|
+
### Step 3.2.2: Add OAuth Environment Variables
|
|
516
|
+
|
|
517
|
+
Add to your `.env.local`:
|
|
518
|
+
|
|
519
|
+
```env
|
|
520
|
+
# NextAuth.js configuration (REQUIRED for OAuth)
|
|
521
|
+
NEXTAUTH_SECRET=your_secure_random_string_at_least_32_characters
|
|
522
|
+
NEXTAUTH_URL=http://localhost:3000 # Change to production URL in production
|
|
523
|
+
|
|
524
|
+
# Google OAuth credentials (from Google Cloud Console)
|
|
525
|
+
HAZO_AUTH_GOOGLE_CLIENT_ID=your_google_client_id_from_step_1
|
|
526
|
+
HAZO_AUTH_GOOGLE_CLIENT_SECRET=your_google_client_secret_from_step_1
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
**Generate NEXTAUTH_SECRET:**
|
|
530
|
+
```bash
|
|
531
|
+
openssl rand -base64 32
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Step 3.2.3: Run OAuth Database Migration
|
|
535
|
+
|
|
536
|
+
Add OAuth fields to the `hazo_users` table:
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
npm run migrate migrations/005_add_oauth_fields_to_hazo_users.sql
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
**Verify migration applied:**
|
|
543
|
+
|
|
544
|
+
**PostgreSQL:**
|
|
545
|
+
```sql
|
|
546
|
+
SELECT column_name FROM information_schema.columns
|
|
547
|
+
WHERE table_name = 'hazo_users'
|
|
548
|
+
AND column_name IN ('google_id', 'auth_providers');
|
|
549
|
+
-- Expected: 2 rows returned
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
**SQLite:**
|
|
553
|
+
```bash
|
|
554
|
+
sqlite3 data/hazo_auth.sqlite ".schema hazo_users" | grep -E "google_id|auth_providers"
|
|
555
|
+
# Expected: google_id TEXT UNIQUE, auth_providers TEXT DEFAULT 'email'
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
**Manual migration (if needed):**
|
|
559
|
+
|
|
560
|
+
**PostgreSQL:**
|
|
561
|
+
```sql
|
|
562
|
+
ALTER TABLE hazo_users ADD COLUMN google_id TEXT UNIQUE;
|
|
563
|
+
ALTER TABLE hazo_users ADD COLUMN auth_providers TEXT DEFAULT 'email';
|
|
564
|
+
CREATE INDEX IF NOT EXISTS idx_hazo_users_google_id ON hazo_users(google_id);
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
**SQLite:**
|
|
568
|
+
```sql
|
|
569
|
+
ALTER TABLE hazo_users ADD COLUMN google_id TEXT;
|
|
570
|
+
ALTER TABLE hazo_users ADD COLUMN auth_providers TEXT DEFAULT 'email';
|
|
571
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_hazo_users_google_id_unique ON hazo_users(google_id);
|
|
572
|
+
CREATE INDEX IF NOT EXISTS idx_hazo_users_google_id ON hazo_users(google_id);
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Step 3.2.4: Configure OAuth in hazo_auth_config.ini
|
|
576
|
+
|
|
577
|
+
Add (or modify) the OAuth section:
|
|
578
|
+
|
|
579
|
+
```ini
|
|
580
|
+
[hazo_auth__oauth]
|
|
581
|
+
# Enable Google OAuth login (default: true)
|
|
582
|
+
enable_google = true
|
|
583
|
+
|
|
584
|
+
# Enable traditional email/password login (default: true)
|
|
585
|
+
enable_email_password = true
|
|
586
|
+
|
|
587
|
+
# Auto-link Google login to existing unverified email/password accounts (default: true)
|
|
588
|
+
auto_link_unverified_accounts = true
|
|
589
|
+
|
|
590
|
+
# Customize button text (optional)
|
|
591
|
+
google_button_text = Continue with Google
|
|
592
|
+
oauth_divider_text = or
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**Configuration Options:**
|
|
596
|
+
|
|
597
|
+
- **Google-only authentication** (no email/password):
|
|
598
|
+
```ini
|
|
599
|
+
enable_google = true
|
|
600
|
+
enable_email_password = false
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
- **Email/password only** (no OAuth):
|
|
604
|
+
```ini
|
|
605
|
+
enable_google = false
|
|
606
|
+
enable_email_password = true
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
- **Both methods** (recommended):
|
|
610
|
+
```ini
|
|
611
|
+
enable_google = true
|
|
612
|
+
enable_email_password = true
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
### Step 3.2.5: Create OAuth API Routes
|
|
616
|
+
|
|
617
|
+
**Option A: Use CLI generator (Recommended):**
|
|
618
|
+
```bash
|
|
619
|
+
npx hazo_auth generate-routes --oauth
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**Option B: Create manually:**
|
|
623
|
+
|
|
624
|
+
Create `app/api/auth/[...nextauth]/route.ts`:
|
|
625
|
+
```typescript
|
|
626
|
+
export { GET, POST } from "hazo_auth/server/routes/nextauth";
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Create `app/api/hazo_auth/oauth/google/callback/route.ts`:
|
|
630
|
+
```typescript
|
|
631
|
+
export { GET } from "hazo_auth/server/routes/oauth_google_callback";
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
Create `app/api/hazo_auth/set_password/route.ts`:
|
|
635
|
+
```typescript
|
|
636
|
+
export { POST } from "hazo_auth/server/routes/set_password";
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### Step 3.2.6: Verify OAuth API Routes
|
|
640
|
+
|
|
641
|
+
```bash
|
|
642
|
+
ls app/api/auth/\[...nextauth\]/route.ts
|
|
643
|
+
ls app/api/hazo_auth/oauth/google/callback/route.ts
|
|
644
|
+
ls app/api/hazo_auth/set_password/route.ts
|
|
645
|
+
# All files should exist
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
**OAuth Setup Checklist:**
|
|
649
|
+
- [ ] Google OAuth credentials obtained
|
|
650
|
+
- [ ] `NEXTAUTH_SECRET` and `NEXTAUTH_URL` set in `.env.local`
|
|
651
|
+
- [ ] `HAZO_AUTH_GOOGLE_CLIENT_ID` and `HAZO_AUTH_GOOGLE_CLIENT_SECRET` set
|
|
652
|
+
- [ ] OAuth migration applied (google_id and auth_providers columns added)
|
|
653
|
+
- [ ] `[hazo_auth__oauth]` section configured in `hazo_auth_config.ini`
|
|
654
|
+
- [ ] OAuth API routes created (`[...nextauth]`, `oauth/google/callback`, `set_password`)
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
495
658
|
## Phase 4: API Routes
|
|
496
659
|
|
|
497
660
|
Create API route files in your project. Each file re-exports handlers from hazo_auth.
|
|
@@ -954,6 +1117,46 @@ Visit each page and verify it loads:
|
|
|
954
1117
|
- [ ] `http://localhost:3000/hazo_auth/register` - Registration form displays
|
|
955
1118
|
- [ ] `http://localhost:3000/hazo_auth/forgot_password` - Forgot password form displays
|
|
956
1119
|
- [ ] `http://localhost:3000/hazo_auth/my_settings` - Settings page displays (after login)
|
|
1120
|
+
- [ ] `http://localhost:3000/hazo_auth/profile_stamp_test` - ProfileStamp component examples display
|
|
1121
|
+
|
|
1122
|
+
### Test 7: Google OAuth (if configured)
|
|
1123
|
+
|
|
1124
|
+
If you completed Phase 3.2 (Google OAuth Setup):
|
|
1125
|
+
|
|
1126
|
+
**Check login page:**
|
|
1127
|
+
1. Visit `http://localhost:3000/hazo_auth/login`
|
|
1128
|
+
2. Verify "Sign in with Google" button appears
|
|
1129
|
+
3. Verify divider with "or" text (if email/password is also enabled)
|
|
1130
|
+
|
|
1131
|
+
**Test Google sign-in:**
|
|
1132
|
+
1. Click "Sign in with Google" button
|
|
1133
|
+
2. You should be redirected to Google's login page
|
|
1134
|
+
3. Sign in with your Google account
|
|
1135
|
+
4. You should be redirected back to your app and logged in
|
|
1136
|
+
5. Visit `/hazo_auth/my_settings`
|
|
1137
|
+
6. Verify "Connected Accounts" section shows Google as connected
|
|
1138
|
+
|
|
1139
|
+
**Test Google-only user features:**
|
|
1140
|
+
1. If you signed in with Google (and didn't have a password account first):
|
|
1141
|
+
2. Visit `/hazo_auth/my_settings`
|
|
1142
|
+
3. Verify "Set Password" section appears
|
|
1143
|
+
4. Set a password
|
|
1144
|
+
5. Log out and try logging in with email/password (should work)
|
|
1145
|
+
|
|
1146
|
+
**Test forgot password with Google-only user:**
|
|
1147
|
+
1. Create a new user with Google OAuth only
|
|
1148
|
+
2. Try visiting `/hazo_auth/forgot_password`
|
|
1149
|
+
3. Enter the Google user's email
|
|
1150
|
+
4. Should show message: "You registered with Google. Please sign in with Google instead."
|
|
1151
|
+
|
|
1152
|
+
**OAuth Test Checklist:**
|
|
1153
|
+
- [ ] "Sign in with Google" button appears on login page
|
|
1154
|
+
- [ ] OAuth divider appears (if both auth methods enabled)
|
|
1155
|
+
- [ ] Google OAuth flow completes successfully
|
|
1156
|
+
- [ ] User is logged in after OAuth callback
|
|
1157
|
+
- [ ] Connected Accounts section shows Google in My Settings
|
|
1158
|
+
- [ ] Set Password feature works for Google-only users
|
|
1159
|
+
- [ ] Forgot password shows appropriate message for Google-only users
|
|
957
1160
|
|
|
958
1161
|
---
|
|
959
1162
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/app/api/hazo_auth/forgot_password/route.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/app/api/hazo_auth/forgot_password/route.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;IAoH9C"}
|
|
@@ -49,6 +49,21 @@ export async function POST(request) {
|
|
|
49
49
|
message: "If an account with that email exists, a password reset link has been sent.",
|
|
50
50
|
}, { status: 200 });
|
|
51
51
|
}
|
|
52
|
+
// Check if this is a Google-only user (no password set)
|
|
53
|
+
if (result.no_password_set) {
|
|
54
|
+
logger.info("password_reset_no_password_set", {
|
|
55
|
+
filename: get_filename(),
|
|
56
|
+
line_number: get_line_number(),
|
|
57
|
+
email,
|
|
58
|
+
note: "User does not have a password set (OAuth-only account)",
|
|
59
|
+
});
|
|
60
|
+
// Return success to prevent email enumeration, but include flag for UI handling
|
|
61
|
+
return NextResponse.json({
|
|
62
|
+
success: true,
|
|
63
|
+
no_password_set: true,
|
|
64
|
+
message: "If an account with that email exists, a password reset link has been sent.",
|
|
65
|
+
}, { status: 200 });
|
|
66
|
+
}
|
|
52
67
|
logger.info("password_reset_requested", {
|
|
53
68
|
filename: get_filename(),
|
|
54
69
|
line_number: get_line_number(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/app/api/hazo_auth/logout/route.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/app/api/hazo_auth/logout/route.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;IAkH9C"}
|
|
@@ -32,6 +32,37 @@ export async function POST(request) {
|
|
|
32
32
|
expires: new Date(0),
|
|
33
33
|
path: "/",
|
|
34
34
|
});
|
|
35
|
+
// Clear NextAuth session cookies (for OAuth users)
|
|
36
|
+
response.cookies.set("next-auth.session-token", "", {
|
|
37
|
+
expires: new Date(0),
|
|
38
|
+
path: "/",
|
|
39
|
+
});
|
|
40
|
+
response.cookies.set("next-auth.csrf-token", "", {
|
|
41
|
+
expires: new Date(0),
|
|
42
|
+
path: "/",
|
|
43
|
+
});
|
|
44
|
+
response.cookies.set("next-auth.callback-url", "", {
|
|
45
|
+
expires: new Date(0),
|
|
46
|
+
path: "/",
|
|
47
|
+
});
|
|
48
|
+
// Also clear secure cookie variants (used in production with HTTPS)
|
|
49
|
+
response.cookies.set("__Secure-next-auth.session-token", "", {
|
|
50
|
+
expires: new Date(0),
|
|
51
|
+
path: "/",
|
|
52
|
+
});
|
|
53
|
+
response.cookies.set("__Secure-next-auth.csrf-token", "", {
|
|
54
|
+
expires: new Date(0),
|
|
55
|
+
path: "/",
|
|
56
|
+
});
|
|
57
|
+
response.cookies.set("__Secure-next-auth.callback-url", "", {
|
|
58
|
+
expires: new Date(0),
|
|
59
|
+
path: "/",
|
|
60
|
+
});
|
|
61
|
+
// Host-prefixed variants (for some NextAuth configurations)
|
|
62
|
+
response.cookies.set("__Host-next-auth.csrf-token", "", {
|
|
63
|
+
expires: new Date(0),
|
|
64
|
+
path: "/",
|
|
65
|
+
});
|
|
35
66
|
// Invalidate user cache
|
|
36
67
|
if (user_id) {
|
|
37
68
|
try {
|
|
@@ -14,6 +14,9 @@ import { NextRequest, NextResponse } from "next/server";
|
|
|
14
14
|
* email_verified: boolean,
|
|
15
15
|
* last_logon: string | undefined,
|
|
16
16
|
* profile_picture_url: string | null,
|
|
17
|
+
* profile_image: string | null, // alias for profile_picture_url
|
|
18
|
+
* avatar_url: string | null, // alias for profile_picture_url
|
|
19
|
+
* image: string | null, // alias for profile_picture_url
|
|
17
20
|
* profile_source: "upload" | "library" | "gravatar" | "custom" | undefined,
|
|
18
21
|
* user: { id, email_address, name, is_active, profile_picture_url },
|
|
19
22
|
* permissions: string[],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/app/api/hazo_auth/me/route.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AASxD
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../src/app/api/hazo_auth/me/route.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AASxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;IA2F7C"}
|