# Microsoft Graph Contact Integration for Laravel

This documentation covers the Microsoft Graph integration system that synchronizes committees and workgroups as contact folders with user contacts in a flat, root-level structure.

## 📁 Contact-Based Approach

The system uses a **contact-based approach** to organize data in Microsoft Graph:

### Flat Structure (Root Level)
- **Committee Folders** → Contact Folders at root level (e.g., "Gas Committee (committee)")
- **WorkGroup Folders** → Contact Folders at root level (e.g., "WG Implementation (workgroup)")
- **User Contacts** → Contacts within appropriate committee/workgroup folders

### Key Benefits
- ✅ **No domain verification required**
- ✅ **Simpler setup and maintenance**
- ✅ **Flat, intuitive folder structure**
- ✅ **Automatic cleanup of orphaned data**
- ✅ **Daily synchronization with membership changes**
- ✅ **Direct folder access without nested navigation**ntact Integration for Laravel

This documentation covers the Microsoft Graph integration system that synchronizes committees and workgroups as contact folders with user contacts in a hierarchical structure.

## � Contact-Based Approach

The system uses a **contact-based approach** to organize data in Microsoft Graph:

### Hierarchical Structure
- **Members** → Root Contact Folder
  - **Committees** → Contact Subfolders within Members
  - **WorkGroups** → Contact Subfolders within Members  
  - **Users** → Contacts within appropriate subfolders

### Key Benefits
- ✅ **No domain verification required**
- ✅ **Simpler setup and maintenance**
- ✅ **Preserves organizational structure**
- ✅ **Automatic cleanup of orphaned data**
- ✅ **Daily synchronization with membership changes**

## ✅ Quick Setup Guide

### Step 1: Azure App Registration

1. **Go to Azure Portal**: https://portal.azure.com
2. **Navigate to**: Azure Active Directory → App registrations → New registration
3. **Configure**:
   - Name: `EFET Intranet Contact Sync`
   - Supported account types: `Accounts in this organizational directory only`
   - Redirect URI: `http://your-domain.com/auth/microsoft/callback` (or localhost for testing)
4. **Click**: Register

### Step 2: Configure API Permissions

1. **Go to**: API permissions → Add a permission → Microsoft Graph → Application permissions
2. **Add these permissions**:
   - `Contacts.ReadWrite` - Create and manage contacts
   - `Organization.Read.All` - Read organization information
3. **Important**: Click "Grant admin consent" for your organization

### Step 3: Create Client Secret

1. **Go to**: Certificates & secrets → New client secret
2. **Description**: `EFET Intranet Contact Sync Secret`
3. **Expires**: Choose appropriate expiration (12 months recommended)
4. **Copy the secret value** - you won't see it again!

### Step 4: Get Required IDs

From your app registration overview page, copy:
- **Application (client) ID**
- **Directory (tenant) ID** 

### Step 5: Configure Environment Variables

Add the following variables to your `.env` file:

```env
# Microsoft Graph Configuration
MSGRAPH_TENANT_ID=your-tenant-id-here
MSGRAPH_CLIENT_ID=your-client-id-here
MSGRAPH_CLIENT_SECRET=your-client-secret-here
MSGRAPH_REDIRECT_URI=http://localhost/auth/microsoft/callback
```

### Step 6: Run Database Migrations

Execute the migrations to add `ms_contact_folder_id` fields:

```bash
lando artisan migrate --path=database/migrations/project
```

This will add the following fields:
- `committees.ms_contact_folder_id` - Microsoft Graph Contact Folder ID
- `work_groups.ms_contact_folder_id` - Microsoft Graph Contact Folder ID

## Usage

### Test Connection

Before running sync operations, test your Microsoft Graph connection:

```bash
lando artisan msgraph:sync --test-connection
```

This will verify:
- API connectivity to Microsoft Graph
- Tenant access and information
- Required permissions (Contacts.ReadWrite, Organization.Read.All)
- Authentication credentials

### Synchronization Commands

#### Complete Contact Synchronization (Recommended)
```bash
# Full root-level sync: folders → cleanup → contacts
lando artisan msgraph:sync
```

#### Individual Operations
```bash
# Sync only contact folders (committees/workgroups at root level)
lando artisan msgraph:sync --contact-folders

# Sync only contacts within existing folders
lando artisan msgraph:sync --contacts

# Delete ALL contact folders (DESTRUCTIVE - requires confirmation)
lando artisan msgraph:delete-folders

# Clean up database ms_id values
lando artisan msgraph:sync --cleanup-database --confirm-deletion
```

#### Dry Run Mode
```bash
# Test what would be synced without making changes
lando artisan msgraph:sync --dry-run
```

#### Utility Commands
```bash
# Sync existing folder IDs from Microsoft Graph to database
lando artisan msgraph:sync --sync-folder-ids

# List all contact folders
lando artisan msgraph:sync --list-folders

# List contacts from a specific folder
lando artisan msgraph:sync --list-contacts="folder-id-or-name"

# List all contacts from all folders
lando artisan msgraph:sync --list-all-contacts

# Clear all contacts from existing folders (without deleting folders)
lando artisan msgraph:sync --clear-contacts

# Test contact folder creation
lando artisan msgraph:sync --test-contact-folder="Test-Folder"

# Diagnose sync issues and discrepancies
lando artisan msgraph:diagnose --detailed
```

# List contacts from a specific folder
lando artisan msgraph:sync --list-contacts=folder-id-here

# Filter folder listings by name
lando artisan msgraph:sync --list-all-folders --filter-folder="Committee"
```

#### Destructive Operations (Use with caution)
```bash
# Delete ALL contact folders (requires confirmation)
lando artisan msgraph:sync --delete-all-folders --confirm-deletion

# Delete specific contact folders by ID
lando artisan msgraph:sync --delete-folders=id1,id2,id3 --confirm-deletion

# Clean up ALL ms_contact_folder_id values from database
lando artisan msgraph:sync --cleanup-database --confirm-deletion
```

### Automated Scheduling

Add this to your `app/Console/Kernel.php` for daily synchronization:

```php
protected function schedule(Schedule $schedule)
{
    // Daily complete sync at 6 AM
    $schedule->command('msgraph:sync')
             ->dailyAt('06:00')
             ->timezone('America/New_York');
             
    // Weekly cleanup of orphaned folders on Sunday at 3 AM
    $schedule->command('msgraph:sync --cleanup-orphaned')
             ->weekly()
             ->sundays()
             ->at('03:00');
}
```

## Sync Process Flow

### Daily Complete Sync (`msgraph:sync`)

1. **📁 Contact Folders Sync**
   - Creates/updates committee folders at root level
   - Creates/updates workgroup folders at root level  
   - Updates folder names if changed
   - All folders use format: "Committee Name (committee)" or "WorkGroup Name (workgroup)"

2. **🧹 Database Cleanup**
   - Clears ms_id values for unpublished/deleted committees/workgroups
   - Prepares database for clean sync state
   - Logs all cleanup operations

3. **📧 Contacts Sync**
   - **Adds new members** to appropriate folders
   - **Updates existing contacts** when user data changes
   - **Removes contacts** when users leave committees/workgroups
   - Handles multiple folder memberships correctly

### Data Synchronization Logic

#### Contact Folders
- **Committee Published** → Create contact folder at root level
- **Committee Title Changed** → Update folder displayName
- **Committee Unpublished/Deleted** → Clear ms_id (folder cleanup via separate command)

#### Contacts  
- **User Joins Committee** → Add contact to committee folder
- **User Leaves Committee** → Remove contact from committee folder
- **User Data Changes** → Update contact information
- **User Profile Updates** → Sync displayName, email, etc.

## Service Architecture

### Core Classes

#### `MicrosoftGraphSyncService`
Main service handling all Microsoft Graph operations:

```php
// Complete synchronization
$results = $syncService->syncAll($dryRun = false);

// Individual operations
$results = $syncService->syncContactFolders($dryRun = false);
$results = $syncService->syncContacts($dryRun = false);
$results = $syncService->cleanupOrphanedContactFolders($dryRun = false);
```

#### `SyncMicrosoftGraphCommand`
Artisan command providing CLI interface with comprehensive options.

#### `MockMicrosoftGraphService`
Mock service for development and testing without hitting real Microsoft Graph API.

### Error Handling

The system includes comprehensive error handling:

- **API Rate Limiting**: Automatic retry with exponential backoff
- **Network Issues**: Graceful degradation and detailed logging
- **Permission Errors**: Clear error messages and troubleshooting steps
- **Data Validation**: Input sanitization and validation before API calls
- **Transaction Support**: Database transactions for data consistency

### Logging

All operations are logged with appropriate levels:

```php
// Info: Normal operations
Log::info("📁 Contact folder 'Committee Name' created");

// Warning: Non-critical issues  
Log::warning("⚠️ Contact folder already exists, skipping");

// Error: Failed operations
Log::error("❌ Failed to create contact: API rate limit exceeded");
```

## API Response Formats

### `syncAll()` Return Format
```php
[
    'contact_folders' => [
        'created' => 5,
        'updated' => 3, 
        'errors' => 0
    ],
    'orphaned_cleanup' => [
        'deleted' => 2,
        'errors' => 0
    ],
    'contacts' => [
        'created' => 15,
        'updated' => 8,
        'removed' => 3,
        'errors' => 0
    ]
]
```

### `testConnection()` Return Format
```php
[
    'connected' => true,
    'tenant' => [
        'id' => 'tenant-id',
        'displayName' => 'Organization Name',
        'verifiedDomains' => [...]
    ],
    'permissions' => [
        'Contacts.ReadWrite - ✅',
        'Organization.Read.All - ✅'
    ],
    'error' => null
]
```

## Database Schema Changes

### Committees Table
```sql
ALTER TABLE committees ADD COLUMN ms_contact_folder_id VARCHAR(255) NULL UNIQUE 
COMMENT 'Microsoft Graph Contact Folder ID';
```

### Work Groups Table
```sql
ALTER TABLE work_groups ADD COLUMN ms_contact_folder_id VARCHAR(255) NULL UNIQUE 
COMMENT 'Microsoft Graph Contact Folder ID';
```

## Troubleshooting

### Common Issues

#### "Permission denied" errors
- Verify app registration has `Contacts.ReadWrite` permission
- Ensure admin consent has been granted
- Check tenant ID and client credentials

#### "Contact folder not found" errors
- Run `msgraph:sync --sync-folder-ids` to sync existing folder IDs
- Verify folders exist in Microsoft Graph with `--list-all-folders`

#### Duplicate contacts
- Check for multiple committee/workgroup memberships
- Review user email uniqueness in database

### Debug Commands

```bash
# Test connection and permissions
lando artisan msgraph:sync --test-connection

# List all folders to verify structure
lando artisan msgraph:sync --list-all-folders --detailed-folders

# Check specific folder contents
lando artisan msgraph:sync --list-contacts=folder-id

# Dry run to see what would change
lando artisan msgraph:sync --dry-run
```

## Support & Maintenance

### Regular Maintenance
- Monitor sync logs daily for errors
- Review orphaned cleanup results weekly
- Test connection after Azure app changes
- Update credentials before expiration

### Performance Considerations
- Daily sync processes all data for accuracy
- Cleanup operations run efficiently with minimal API calls
- Dry-run mode available for testing without changes
- Background sync during low-usage hours recommended

### Version Compatibility
- Laravel 8.x+
- PHP 7.4+
- Microsoft Graph API v1.0
- dcblogdev/laravel-microsoft-graph package

## Changelog

### Version 2.0.0 (2025-06-18)
- **BREAKING**: Removed legacy user/group sync approach
- Contact-based approach only (hierarchical folders)
- Automatic orphaned folder cleanup
- Improved error handling and logging
- Simplified command interface
- Daily complete synchronization with membership tracking

### Version 1.0.0 (2025-06-05)
- Initial implementation with dual approaches
- User, committee, and workgroup synchronization  
- Contact folders alternative approach
- Comprehensive Artisan command interface

2. **Required Permissions**
   - `User.ReadWrite.All` - To create and manage users
   - `Group.ReadWrite.All` - To create and manage groups
   - `GroupMember.ReadWrite.All` - To manage group memberships
   - `Organization.Read.All` - To read organization information

## Installation & Setup

### 1. Install the Laravel Microsoft Graph Package

The system uses the `dcblogdev/laravel-microsoft-graph` package which should already be installed.

### 2. Environment Configuration

Add the following variables to your `.env` file:

```env
# Microsoft Graph Configuration
MSGRAPH_TENANT_ID=your-tenant-id-here
MSGRAPH_CLIENT_ID=your-client-id-here
MSGRAPH_CLIENT_SECRET=your-client-secret-here
MSGRAPH_REDIRECT_URI=http://localhost/auth/microsoft/callback

# Optional: Restrict sync to specific domains (comma-separated)
MSGRAPH_ALLOWED_DOMAINS=company.com,partner.com

# Default password for new Microsoft users
MSGRAPH_DEFAULT_PASSWORD=TempPass123!
```

### 3. Run Database Migrations

Execute the migrations to add `ms_id` fields to your tables:

```bash
lando artisan migrate --path=database/migrations/project
```

This will add the following fields:
- `users.ms_id` - Microsoft Graph User ID
- `committees.ms_id` - Microsoft Graph Group ID
- `work_groups.ms_id` - Microsoft Graph Group ID

## Usage

### Connection Testing

Before running any synchronization, it's recommended to test your Microsoft Graph API connection:

```bash
lando artisan msgraph:sync --test-connection
```

This will verify:
- API connectivity to Microsoft Graph
- Tenant access and information
- Required permissions (User.ReadWrite.All, Group.ReadWrite.All, etc.)
- Authentication credentials

### Manual Synchronization

#### Contact-Based Sync (Recommended for avoiding domain verification)
```bash
# Create contact folders for committees and workgroups
lando artisan msgraph:sync --contact-folders

# Create contacts for users within the folders
lando artisan msgraph:sync --contacts

# Full contact-based sync
lando artisan msgraph:sync
```

#### Traditional User/Group Sync (Requires domain verification)
```bash
# Sync only users
lando artisan msgraph:sync --users

# Sync only committees as groups
lando artisan msgraph:sync --committees

# Sync only workgroups as groups
lando artisan msgraph:sync --workgroups

# Sync only memberships
lando artisan msgraph:sync --memberships
```

#### Dry Run Mode
```bash
# Test what would be synced without making changes
lando artisan msgraph:sync --dry-run
```

#### Test Connection
```bash
# Test Microsoft Graph API connection and permissions
lando artisan msgraph:sync --test-connection
```

### Automatic Synchronization

The system is configured to run automatically every day at 2:00 AM via Laravel's task scheduler.

To enable automatic sync, ensure your server's cron is configured:

```bash
* * * * * cd /path-to-your-project && lando artisan schedule:run >> /dev/null 2>&1
```

## How It Works

### 1. User Synchronization (`syncUsers()`)

- Identifies users who are members of committees or workgroups
- Creates new users in Microsoft Entra ID if they don't exist
- Updates existing users if their information has changed
- Stores Microsoft Graph User ID in the `ms_id` field
- Only syncs users with valid email addresses
- Generates secure temporary passwords for new users

**User Creation Process:**
1. Check if user already exists in Microsoft Graph
2. Generate display name and user principal name
3. Create user with temporary password
4. Store Microsoft ID locally
5. Log success/failure

### 2. Committee Synchronization (`syncCommittees()`)

- Creates Microsoft Groups for each published committee
- Updates group information if changed
- Stores Microsoft Graph Group ID in the `ms_id` field
- Uses committee title as group display name
- Generates unique group nicknames

**Group Creation Process:**
1. Check if group already exists in Microsoft Graph
2. Create group with appropriate settings
3. Store Microsoft ID locally
4. Log success/failure

### 3. WorkGroup Synchronization (`syncWorkGroups()`)

- Creates Microsoft Groups for each published workgroup
- Links workgroups to their parent committee groups
- Updates group information if changed
- Stores Microsoft Graph Group ID in the `ms_id` field

### 4. Membership Synchronization (`syncMemberships()`)

- Adds users to appropriate committee and workgroup groups
- Only processes users and groups that have been successfully synced
- Maintains consistency between local database and Microsoft Graph
- Handles membership errors gracefully

**Membership Process:**
1. Get all committee-user relationships
2. Add users to corresponding Microsoft Groups
3. Get all workgroup-user relationships
4. Add users to corresponding Microsoft Groups
5. Log success/failure for each operation

## Error Handling

The system includes comprehensive error handling:

- **Database Transactions**: All operations are wrapped in transactions
- **Detailed Logging**: All operations are logged with context
- **Graceful Failures**: Individual failures don't stop the entire sync
- **Retry Logic**: Built-in retry for transient failures
- **Statistics Tracking**: Detailed reporting of sync results

## Security Considerations

### Authentication
- Uses OAuth 2.0 client credentials flow
- Secure token management via the Microsoft Graph package
- Automatic token refresh

### Data Protection
- Only syncs users who are actually members of committees/workgroups
- Respects domain restrictions if configured
- Generates secure temporary passwords
- Stores minimal user information in Microsoft Graph

### Access Control
- Requires appropriate Microsoft Graph permissions
- Uses least-privilege principle
- Separate permissions for users, groups, and memberships

## Monitoring & Logging

### Log Locations
- Application logs: `storage/logs/laravel.log`
- Sync operations are logged with context
- Both success and failure events are recorded

### Key Log Events
- Sync start/completion
- User creation/updates
- Group creation/updates
- Membership additions
- Error conditions
- Performance metrics

### Sample Log Entry
```
[2025-06-05 14:30:00] local.INFO: 🚀 Starting complete synchronization with Microsoft Graph
[2025-06-05 14:30:05] local.INFO: 👤 User created in Microsoft Graph {"user_id":123,"ms_id":"abc-123","email":"user@example.com"}
[2025-06-05 14:30:10] local.INFO: ✅ Synchronization completed successfully {"users":{"created":5,"updated":2,"errors":0},...}
```

## Troubleshooting

### Common Issues

#### Permission Errors
```
Error: Insufficient privileges to complete the operation
```
**Solution**: Verify your app registration has the required permissions and admin consent has been granted.

#### User Already Exists
```
Error: Another object with the same value for property userPrincipalName already exists
```
**Solution**: This usually means the user exists but wasn't previously synced. The system will attempt to find and link the existing user.

#### Domain Restrictions
```
Error: The domain is not verified
```
**Solution**: Ensure the email domains are verified in your Azure tenant, or configure `MSGRAPH_ALLOWED_DOMAINS`.

### Debug Mode

Enable more detailed logging by setting:
```env
LOG_LEVEL=debug
```

### Testing Configuration

Test your Microsoft Graph connection:
```bash
# Test API connectivity and permissions
lando artisan msgraph:sync --test-connection

# Test sync configuration without making changes
lando artisan msgraph:sync --dry-run --users
```

## Performance Considerations

### Batch Operations
- Users are processed individually for better error handling
- Groups are created/updated one at a time
- Memberships are added individually with error recovery

### Rate Limiting
- The Microsoft Graph package handles rate limiting automatically
- Implements exponential backoff for retries
- Respects Microsoft Graph throttling limits

### Optimization Tips
- Run sync during off-peak hours (configured for 2:00 AM)
- Use `--dry-run` for testing before actual sync
- Monitor logs for performance bottlenecks
- Consider splitting large sync operations

## API Reference

### MicrosoftGraphSyncService Methods

#### `syncAll($dryRun = false): array`
Synchronizes all components (users, committees, workgroups, memberships)

#### `syncUsers($dryRun = false): array`
Synchronizes only users

#### `syncCommittees($dryRun = false): array`
Synchronizes only committees

#### `syncWorkGroups($dryRun = false): array`
Synchronizes only workgroups

#### `syncMemberships($dryRun = false): array`
Synchronizes only memberships

#### `testConnection(): array`
Tests Microsoft Graph API connectivity and permissions

### Return Format
All sync methods return an array with statistics:
```php
[
    'created' => 5,    // Number of items created
    'updated' => 2,    // Number of items updated
    'errors' => 0      // Number of errors encountered
]
```

#### `testConnection()` Return Format
```php
[
    'connected' => true,              // API connection status
    'tenant' => [                     // Tenant information
        'id' => 'tenant-id',
        'displayName' => 'Organization Name',
        'verifiedDomains' => [...]
    ],
    'permissions' => [                // Permission test results
        'User.Read - ✅',
        'User.ReadWrite.All - ✅',
        'Group.ReadWrite.All - ✅',
        // ...
    ],
    'error' => null                   // Error message if connection failed
]
```

## Database Schema Changes

### Users Table
```sql
ALTER TABLE users ADD COLUMN ms_id VARCHAR(255) NULL UNIQUE COMMENT 'Microsoft Graph User ID';
```

### Committees Table
```sql
ALTER TABLE committees ADD COLUMN ms_id VARCHAR(255) NULL UNIQUE COMMENT 'Microsoft Graph Group ID';
```

### Work Groups Table
```sql
ALTER TABLE work_groups ADD COLUMN ms_id VARCHAR(255) NULL UNIQUE COMMENT 'Microsoft Graph Group ID';
```

## Support & Maintenance

### Regular Maintenance
- Monitor sync logs daily
- Review failed operations
- Update Microsoft Graph permissions if needed
- Test sync operations after Laravel updates

### Backup Considerations
- `ms_id` fields should be included in database backups
- Microsoft Graph data is not backed up locally
- Consider export procedures for Microsoft Graph data

### Version Compatibility
- Laravel 8.x+
- PHP 7.4+
- Microsoft Graph API v1.0
- dcblogdev/laravel-microsoft-graph package

## Changelog

### Version 1.0.0 (2025-06-05)
- Initial implementation
- User, committee, and workgroup synchronization
- Membership management
- Artisan command interface
- Connection testing functionality
- Automated scheduling
- Comprehensive error handling and logging
