Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions PROFILE_PICTURES_FEATURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Profile Picture Management Feature

This implementation adds the ability for users to manage their profile pictures from multiple social login sources.

## Features Implemented

### 1. Profile Picture Array
- Added `profilePictures` array to User model to store multiple profile pictures
- Each picture includes:
- `source`: The provider (gravatar, facebook, google, github, etc.)
- `url`: The picture URL
- `isSelected`: Boolean indicating if this is the currently selected picture

### 2. Automatic Picture Collection
- When users link social accounts, their profile pictures are automatically added to the array
- Gravatar is always available as a fallback option
- The first linked account's picture becomes the default selection

### 3. Profile Picture Selection Interface
- Users can view all available profile pictures in their account settings
- Radio button interface allows selecting which picture to use
- Pictures are displayed as thumbnails with the source provider name

### 4. Account Unlinking Integration
- When users unlink a social account, the corresponding profile picture is removed
- If the removed picture was selected, the system automatically selects another available picture
- Gravatar is used as the ultimate fallback

## Files Modified

### Models
- `models/User.js`: Added profilePictures schema and helper methods

### Controllers
- `controllers/user.js`: Added profile picture selection handler and updated account view

### Views
- `views/account/profile.pug`: Added profile picture selection interface

### Configuration
- `config/passport.js`: Updated all OAuth strategies to use new picture management
- `app.js`: Added route for profile picture updates

## API Endpoints

### POST /account/picture
Updates the selected profile picture for the authenticated user.

**Parameters:**
- `pictureSource`: The source of the picture to select (gravatar, facebook, google, etc.)

**Response:**
- Redirects to `/account` with success/error flash message

## User Model Methods

### addProfilePicture(source, url)
Adds or updates a profile picture from a specific source.

### selectProfilePicture(source)
Selects a profile picture as the active one.

### removeProfilePicture(source)
Removes a profile picture from a specific source and handles fallback selection.

## Usage Example

```javascript
// Add a profile picture
user.addProfilePicture('facebook', 'https://graph.facebook.com/123/picture');

// Select a different picture
user.selectProfilePicture('google');

// Remove a picture
user.removeProfilePicture('facebook');

await user.save();
```

## Migration Notes

- Existing users will automatically get gravatar added to their profile pictures array via middleware
- The current `profile.picture` field is maintained for backward compatibility
- For existing deployments, run the migration script: `node scripts/migrate-profile-pictures.js`
- No database schema migration is required as the new fields have default values

## Security Considerations

- Profile picture URLs are validated to come from trusted OAuth providers
- CSRF protection is maintained for the picture selection form
- User authentication is required for all profile picture operations
38 changes: 35 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ I also tried to make it as **generic** and **reusable** as possible to cover mos

- Login
- **Local Authentication** Sign in with Email and Password, Passwordless
- **OAuth 2.0 Authentication:** Sign in with Google, Facebook, X (Twitter), Twitch, Github, Discord
- **OAuth 2.0 Authentication:** Sign in with Google, Facebook, X (Twitter), Twitch, Github, Discord, Microsoft
- **OpenID Connect:** Sign in with LinkedIn
- **User Profile and Account Management**
- Gravatar
Expand All @@ -90,7 +90,7 @@ I also tried to make it as **generic** and **reusable** as possible to cover mos
- Support for a range of foundational and embedding models (DeepSeek, Llama, Mistral, Sentence Transformers, etc.) via LangChain, Together.AI, and Hugging Face
- **API Examples**
- **Backoffice:** Lob (USPS Mail), Paypal, Quickbooks, Stripe, Twilio (text messaging)
- **Data, Media & Entertainment:** Alpha Vantage (stocks and finance info) with ChartJS, Github, Foursquare, Last.fm, New York Times, PubChem (chemical information), Trakt.tv (movies/TV), Twitch, Tumblr (OAuth 1.0a example), Web Scraping
- **Data, Media & Entertainment:** Alpha Vantage (stocks and finance info) with ChartJS, Github, Foursquare, Last.fm, New York Times, PubChem (chemical information), Trakt.tv (movies/TV), Twitch, Tumblr (OAuth 1.0a example), Web Scraping, Wikipedia
- **Maps and Location:** Google Maps, HERE Maps
- **Productivity:** Google Drive, Google Sheets

Expand Down Expand Up @@ -156,7 +156,7 @@ _What to get and configure:_
- reCAPTCHA
- For contact form submission, but you can skip it during your development
- OAuth for social logins (Sign in with / Login with)
- Depending on your application need, obtain keys from Google, Facebook, X (Twitter), LinkedIn, Twitch, GitHub. You don't have to obtain valid keys for any provider that you don't need. Just remove the buttons and links in the login and account pug views before your demo.
- Depending on your application need, obtain keys from Google, Facebook, X (Twitter), LinkedIn, Twitch, GitHub, Microsoft. You don't have to obtain valid keys for any provider that you don't need. Just remove the buttons and links in the login and account pug views before your demo.
- API keys for service providers that you need in the API Examples if you are planning to use them.

- MongoDB Atlas
Expand Down Expand Up @@ -326,6 +326,25 @@ Obtain SMTP credentials from a provider for transactional emails. Set the SMTP_U

<hr>

<img src="https://upload.wikimedia.org/wikipedia/commons/4/44/Microsoft_logo.svg" height="50">

- Go to <a href="https://portal.azure.com/" target="_blank">Azure Portal</a>
- Sign in with your Microsoft account or create a new one
- Navigate to **Azure Active Directory** > **App registrations**
- Click **+ New registration**
- Enter your application name
- For **Supported account types**, select **Accounts in any organizational directory and personal Microsoft accounts**
- For **Redirect URI**, select **Web** and enter your BASE_URL value followed by `/auth/microsoft/callback` (i.e. `http://localhost:8080/auth/microsoft/callback`)
- Click **Register**
- Copy the **Application (client) ID** and paste it into your `.env` file as `MICROSOFT_CLIENT_ID`
- Click on **Certificates & secrets** in the left sidebar
- Under **Client secrets**, click **+ New client secret**
- Add a description and select an expiration period
- Click **Add** and copy the **Value** (not the Secret ID) immediately and paste it into your `.env` file as `MICROSOFT_CLIENT_SECRET`
- **Important:** The client secret value will only be shown once, so make sure to copy it immediately

<hr>

<img src="https://s3-us-west-2.amazonaws.com/public.lob.com/dashboard/navbar/lob-logo.svg" height="50">

- Visit <a href="https://dashboard.lob.com/register" target="_blank">Lob Dashboard</a>
Expand Down Expand Up @@ -459,6 +478,19 @@ The OpenAI moderation API for checking harmful inputs is free to use as long as

<hr>

<img src="https://upload.wikimedia.org/wikipedia/commons/8/80/Wikipedia-logo-v2.svg" height="75">

**Wikipedia API** uses the public REST API which doesn't require any API keys or authentication. The Wikipedia API provides access to Wikipedia content and metadata.

- **No setup required** - The Wikipedia REST API is completely free and open
- **API Documentation**: Visit <a href="https://en.wikipedia.org/api/rest_v1/" target="_blank">Wikipedia REST API</a> for full documentation
- **Rate Limits**: Please be respectful of Wikipedia's servers and don't make excessive requests
- **Example endpoints used**:
- Search: `https://en.wikipedia.org/api/rest_v1/page/search/{query}`
- Page summary: `https://en.wikipedia.org/api/rest_v1/page/summary/{title}`

<hr>

## Web Analytics

This project supports integrating web analytics tools such as Google Analytics 4 and Facebook Pixel, along with Open Graph metadata for social sharing. Below are instructions to help you set up these features in your application.
Expand Down
6 changes: 6 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ app.get('/account/verify', passportConfig.isAuthenticated, userController.getVer
app.get('/account/verify/:token', passportConfig.isAuthenticated, userController.getVerifyEmailToken);
app.get('/account', passportConfig.isAuthenticated, userController.getAccount);
app.post('/account/profile', passportConfig.isAuthenticated, userController.postUpdateProfile);
app.post('/account/picture', passportConfig.isAuthenticated, userController.postUpdatePicture);
app.post('/account/password', passportConfig.isAuthenticated, userController.postUpdatePassword);
app.post('/account/delete', passportConfig.isAuthenticated, userController.postDeleteAccount);
app.post('/account/logout-everywhere', passportConfig.isAuthenticated, userController.postLogoutEverywhere);
Expand Down Expand Up @@ -232,6 +233,7 @@ app.get('/api/chart', apiController.getChart);
app.get('/api/google/sheets', passportConfig.isAuthenticated, passportConfig.isAuthorized, apiController.getGoogleSheets);
app.get('/api/quickbooks', passportConfig.isAuthenticated, passportConfig.isAuthorized, apiController.getQuickbooks);
app.get('/api/trakt', apiController.getTrakt);
app.get('/api/wikipedia', apiController.getWikipedia);
app.get('/api/pubchem', apiController.getPubChem);

/**
Expand Down Expand Up @@ -303,6 +305,10 @@ app.get('/auth/discord', passport.authenticate('discord'));
app.get('/auth/discord/callback', passport.authenticate('discord', { failureRedirect: '/auth/failure' }), (req, res) => {
res.redirect(req.session.returnTo || '/');
});
app.get('/auth/microsoft', passport.authenticate('microsoft'));
app.get('/auth/microsoft/callback', passport.authenticate('microsoft', { failureRedirect: '/auth/failure' }), (req, res) => {
res.redirect(req.session.returnTo || '/');
});

/**
* OAuth authorization routes. (API examples)
Expand Down
Loading