> ## Documentation Index
> Fetch the complete documentation index at: https://docs.strikebet.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Update User Profile

> Update authenticated user profile information

Update the profile information for the currently authenticated user. This endpoint allows users to modify their personal information, preferences, and user metadata.

<Note>
  This endpoint requires authentication. Include the Bearer token in the Authorization header.
</Note>

<RequestExample>
  ```bash cURL
  curl -X PUT "http://localhost:8080/user" \
    -H "Authorization: Bearer your_access_token_here" \
    -H "Content-Type: application/json" \
    -d '{
      "email": "newemail@example.com",
      "phone": "+1234567890",
      "password": "newpassword123",
      "data": {
        "first_name": "John",
        "last_name": "Doe",
        "avatar_url": "https://example.com/avatar.jpg"
      }
    }'
  ```

  ```javascript JavaScript
  const response = await fetch('http://localhost:8080/user', {
    method: 'PUT',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      email: 'newemail@example.com',
      phone: '+1234567890',
      password: 'newpassword123',
      data: {
        first_name: 'John',
        last_name: 'Doe',
        avatar_url: 'https://example.com/avatar.jpg',
        preferences: {
          theme: 'dark',
          notifications: true
        }
      }
    }),
  });

  const updatedUser = await response.json();
  ```

  ```python Python
  import requests

  headers = {
      'Authorization': f'Bearer {access_token}',
      'Content-Type': 'application/json'
  }

  data = {
      'email': 'newemail@example.com',
      'phone': '+1234567890',
      'password': 'newpassword123',
      'data': {
          'first_name': 'John',
          'last_name': 'Doe',
          'avatar_url': 'https://example.com/avatar.jpg'
      }
  }

  response = requests.put('http://localhost:8080/user', headers=headers, json=data)
  updated_user = response.json()
  ```

  ```go Go
  package main

  import (
      "bytes"
      "encoding/json"
      "net/http"
  )

  func main() {
      data := map[string]interface{}{
          "email": "newemail@example.com",
          "phone": "+1234567890",
          "password": "newpassword123",
          "data": map[string]interface{}{
              "first_name": "John",
              "last_name": "Doe",
              "avatar_url": "https://example.com/avatar.jpg",
          },
      }
      
      jsonData, _ := json.Marshal(data)
      
      req, _ := http.NewRequest("PUT", "http://localhost:8080/user", bytes.NewBuffer(jsonData))
      req.Header.Set("Authorization", "Bearer "+accessToken)
      req.Header.Set("Content-Type", "application/json")
      
      client := &http.Client{}
      resp, err := client.Do(req)
      // Handle response...
  }
  ```
</RequestExample>

## Request Body

<ParamField body="email" type="string">
  New email address. If changed, email verification will be required.
</ParamField>

<ParamField body="phone" type="string">
  New phone number in E.164 format. If changed, phone verification will be required.
</ParamField>

<ParamField body="password" type="string">
  New password. Must meet security requirements (minimum 8 characters).
</ParamField>

<ParamField body="data" type="object">
  User metadata object containing profile information and preferences.
</ParamField>

<ParamField body="nonce" type="string">
  Optional nonce for additional security (prevents replay attacks).
</ParamField>

## Response

Returns the updated user object with the same structure as the GET /user endpoint.

<ResponseField name="id" type="string">
  Unique user identifier (UUID)
</ResponseField>

<ResponseField name="email" type="string">
  Updated email address
</ResponseField>

<ResponseField name="phone" type="string">
  Updated phone number
</ResponseField>

<ResponseField name="user_metadata" type="object">
  Updated user metadata
</ResponseField>

<ResponseField name="updated_at" type="string">
  ISO timestamp when user account was last updated
</ResponseField>

<ResponseExample>
  ```json 200 - Profile Updated
  {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "aud": "authenticated",
    "role": "authenticated",
    "email": "newemail@example.com",
    "email_confirmed_at": null,
    "phone": "+1234567890",
    "phone_confirmed_at": null,
    "confirmed_at": "2024-01-15T10:30:00Z",
    "last_sign_in_at": "2024-01-20T14:22:00Z",
    "app_metadata": {
      "provider": "email",
      "providers": ["email", "phone"]
    },
    "user_metadata": {
      "first_name": "John",
      "last_name": "Doe",
      "avatar_url": "https://example.com/avatar.jpg",
      "preferences": {
        "theme": "dark",
        "notifications": true
      }
    },
    "identities": [
      {
        "id": "123e4567-e89b-12d3-a456-426614174000",
        "user_id": "123e4567-e89b-12d3-a456-426614174000",
        "identity_data": {
          "email": "newemail@example.com",
          "sub": "123e4567-e89b-12d3-a456-426614174000"
        },
        "provider": "email",
        "last_sign_in_at": "2024-01-20T14:22:00Z",
        "created_at": "2024-01-15T10:30:00Z",
        "updated_at": "2024-01-20T16:45:00Z"
      }
    ],
    "created_at": "2024-01-15T10:30:00Z",
    "updated_at": "2024-01-20T16:45:00Z"
  }
  ```
</ResponseExample>

## Error Responses

<ResponseExample>
  ```json 400 - Invalid Data
  {
    "code": 400,
    "msg": "Invalid email format",
    "details": "Please provide a valid email address"
  }
  ```

  ```json 401 - Unauthorized
  {
    "code": 401,
    "msg": "Invalid token",
    "details": "The access token is invalid or expired"
  }
  ```

  ```json 409 - Email Already Exists
  {
    "code": 409,
    "msg": "Email already registered",
    "details": "An account with this email address already exists"
  }
  ```

  ```json 422 - Weak Password
  {
    "code": 422,
    "msg": "Password too weak",
    "details": "Password must be at least 8 characters with uppercase, lowercase, number, and special character"
  }
  ```
</ResponseExample>

## Implementation Examples

### React Profile Edit Form

```jsx
import { useState, useEffect } from 'react';

function ProfileEditForm({ user, onUpdate }) {
  const [formData, setFormData] = useState({
    email: '',
    phone: '',
    password: '',
    confirmPassword: '',
    first_name: '',
    last_name: '',
    avatar_url: '',
    bio: '',
    website: '',
    location: '',
    preferences: {
      theme: 'light',
      notifications: true,
      language: 'en'
    }
  });
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [success, setSuccess] = useState('');

  useEffect(() => {
    if (user) {
      setFormData({
        email: user.email || '',
        phone: user.phone || '',
        password: '',
        confirmPassword: '',
        first_name: user.user_metadata?.first_name || '',
        last_name: user.user_metadata?.last_name || '',
        avatar_url: user.user_metadata?.avatar_url || '',
        bio: user.user_metadata?.bio || '',
        website: user.user_metadata?.website || '',
        location: user.user_metadata?.location || '',
        preferences: {
          theme: user.user_metadata?.preferences?.theme || 'light',
          notifications: user.user_metadata?.preferences?.notifications ?? true,
          language: user.user_metadata?.preferences?.language || 'en'
        }
      });
    }
  }, [user]);

  const handleInputChange = (field, value) => {
    if (field.includes('.')) {
      const [parent, child] = field.split('.');
      setFormData(prev => ({
        ...prev,
        [parent]: {
          ...prev[parent],
          [child]: value
        }
      }));
    } else {
      setFormData(prev => ({
        ...prev,
        [field]: value
      }));
    }
  };

  const validateForm = () => {
    if (formData.password && formData.password !== formData.confirmPassword) {
      setError('Passwords do not match');
      return false;
    }

    if (formData.password && formData.password.length < 8) {
      setError('Password must be at least 8 characters');
      return false;
    }

    if (formData.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
      setError('Please enter a valid email address');
      return false;
    }

    if (formData.phone && !/^\+[1-9]\d{1,14}$/.test(formData.phone)) {
      setError('Please enter a valid phone number in E.164 format');
      return false;
    }

    return true;
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    
    if (!validateForm()) {
      return;
    }

    setLoading(true);
    setError('');
    setSuccess('');

    try {
      const token = localStorage.getItem('access_token');
      
      const updateData = {
        email: formData.email,
        phone: formData.phone,
        data: {
          first_name: formData.first_name,
          last_name: formData.last_name,
          avatar_url: formData.avatar_url,
          bio: formData.bio,
          website: formData.website,
          location: formData.location,
          preferences: formData.preferences
        }
      };

      // Only include password if it's being changed
      if (formData.password) {
        updateData.password = formData.password;
      }

      const response = await fetch('/api/auth/user', {
        method: 'PUT',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(updateData),
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.msg || 'Failed to update profile');
      }

      const updatedUser = await response.json();
      
      setSuccess('Profile updated successfully!');
      onUpdate?.(updatedUser);
      
      // Clear password fields
      setFormData(prev => ({
        ...prev,
        password: '',
        confirmPassword: ''
      }));
      
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="profile-edit-form">
      <h2>Edit Profile</h2>
      
      {error && (
        <div className="error-message">
          {error}
        </div>
      )}
      
      {success && (
        <div className="success-message">
          {success}
        </div>
      )}

      <div className="form-section">
        <h3>Basic Information</h3>
        
        <div className="form-row">
          <div className="form-group">
            <label htmlFor="first_name">First Name</label>
            <input
              type="text"
              id="first_name"
              value={formData.first_name}
              onChange={(e) => handleInputChange('first_name', e.target.value)}
            />
          </div>
          
          <div className="form-group">
            <label htmlFor="last_name">Last Name</label>
            <input
              type="text"
              id="last_name"
              value={formData.last_name}
              onChange={(e) => handleInputChange('last_name', e.target.value)}
            />
          </div>
        </div>

        <div className="form-group">
          <label htmlFor="email">Email Address</label>
          <input
            type="email"
            id="email"
            value={formData.email}
            onChange={(e) => handleInputChange('email', e.target.value)}
          />
          <small>Changing your email will require verification</small>
        </div>

        <div className="form-group">
          <label htmlFor="phone">Phone Number</label>
          <input
            type="tel"
            id="phone"
            value={formData.phone}
            onChange={(e) => handleInputChange('phone', e.target.value)}
            placeholder="+1234567890"
          />
          <small>Use E.164 format (e.g., +1234567890)</small>
        </div>

        <div className="form-group">
          <label htmlFor="avatar_url">Avatar URL</label>
          <input
            type="url"
            id="avatar_url"
            value={formData.avatar_url}
            onChange={(e) => handleInputChange('avatar_url', e.target.value)}
            placeholder="https://example.com/avatar.jpg"
          />
        </div>
      </div>

      <div className="form-section">
        <h3>Additional Information</h3>
        
        <div className="form-group">
          <label htmlFor="bio">Bio</label>
          <textarea
            id="bio"
            value={formData.bio}
            onChange={(e) => handleInputChange('bio', e.target.value)}
            rows={3}
            placeholder="Tell us about yourself..."
          />
        </div>

        <div className="form-group">
          <label htmlFor="website">Website</label>
          <input
            type="url"
            id="website"
            value={formData.website}
            onChange={(e) => handleInputChange('website', e.target.value)}
            placeholder="https://yourwebsite.com"
          />
        </div>

        <div className="form-group">
          <label htmlFor="location">Location</label>
          <input
            type="text"
            id="location"
            value={formData.location}
            onChange={(e) => handleInputChange('location', e.target.value)}
            placeholder="City, Country"
          />
        </div>
      </div>

      <div className="form-section">
        <h3>Preferences</h3>
        
        <div className="form-group">
          <label htmlFor="theme">Theme</label>
          <select
            id="theme"
            value={formData.preferences.theme}
            onChange={(e) => handleInputChange('preferences.theme', e.target.value)}
          >
            <option value="light">Light</option>
            <option value="dark">Dark</option>
            <option value="auto">Auto</option>
          </select>
        </div>

        <div className="form-group">
          <label htmlFor="language">Language</label>
          <select
            id="language"
            value={formData.preferences.language}
            onChange={(e) => handleInputChange('preferences.language', e.target.value)}
          >
            <option value="en">English</option>
            <option value="es">Spanish</option>
            <option value="fr">French</option>
            <option value="de">German</option>
          </select>
        </div>

        <div className="form-group">
          <label className="checkbox-label">
            <input
              type="checkbox"
              checked={formData.preferences.notifications}
              onChange={(e) => handleInputChange('preferences.notifications', e.target.checked)}
            />
            Enable email notifications
          </label>
        </div>
      </div>

      <div className="form-section">
        <h3>Change Password</h3>
        <p className="section-description">
          Leave blank to keep your current password
        </p>
        
        <div className="form-group">
          <label htmlFor="password">New Password</label>
          <input
            type="password"
            id="password"
            value={formData.password}
            onChange={(e) => handleInputChange('password', e.target.value)}
            minLength={8}
          />
          <small>
            Password must be at least 8 characters with uppercase, lowercase, number, and special character
          </small>
        </div>

        <div className="form-group">
          <label htmlFor="confirmPassword">Confirm New Password</label>
          <input
            type="password"
            id="confirmPassword"
            value={formData.confirmPassword}
            onChange={(e) => handleInputChange('confirmPassword', e.target.value)}
          />
        </div>
      </div>

      <div className="form-actions">
        <button type="submit" disabled={loading} className="save-button">
          {loading ? 'Saving...' : 'Save Changes'}
        </button>
        
        <button 
          type="button" 
          onClick={() => window.history.back()}
          className="cancel-button"
        >
          Cancel
        </button>
      </div>
    </form>
  );
}

export default ProfileEditForm;
```

### React Avatar Upload Component

```jsx
import { useState } from 'react';

function AvatarUpload({ currentAvatar, onAvatarUpdate }) {
  const [uploading, setUploading] = useState(false);
  const [preview, setPreview] = useState(currentAvatar);

  const handleFileSelect = async (event) => {
    const file = event.target.files[0];
    if (!file) return;

    // Validate file type
    if (!file.type.startsWith('image/')) {
      alert('Please select an image file');
      return;
    }

    // Validate file size (max 5MB)
    if (file.size > 5 * 1024 * 1024) {
      alert('File size must be less than 5MB');
      return;
    }

    setUploading(true);

    try {
      // Create preview
      const previewUrl = URL.createObjectURL(file);
      setPreview(previewUrl);

      // Upload to your storage service (e.g., AWS S3, Cloudinary)
      const formData = new FormData();
      formData.append('avatar', file);

      const uploadResponse = await fetch('/api/upload/avatar', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
        },
        body: formData,
      });

      if (!uploadResponse.ok) {
        throw new Error('Failed to upload avatar');
      }

      const { url } = await uploadResponse.json();

      // Update user profile with new avatar URL
      const updateResponse = await fetch('/api/auth/user', {
        method: 'PUT',
        headers: {
          'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          data: {
            avatar_url: url
          }
        }),
      });

      if (!updateResponse.ok) {
        throw new Error('Failed to update profile');
      }

      const updatedUser = await updateResponse.json();
      onAvatarUpdate?.(url, updatedUser);

      // Clean up preview URL
      URL.revokeObjectURL(previewUrl);
      
    } catch (error) {
      console.error('Avatar upload error:', error);
      alert('Failed to upload avatar. Please try again.');
      setPreview(currentAvatar); // Reset preview
    } finally {
      setUploading(false);
    }
  };

  return (
    <div className="avatar-upload">
      <div className="avatar-preview">
        {preview ? (
          <img src={preview} alt="Avatar" className="avatar-image" />
        ) : (
          <div className="avatar-placeholder">
            <span>No Image</span>
          </div>
        )}
        
        {uploading && (
          <div className="upload-overlay">
            <div className="spinner"></div>
            <span>Uploading...</span>
          </div>
        )}
      </div>

      <div className="avatar-controls">
        <label htmlFor="avatar-input" className="upload-button">
          {uploading ? 'Uploading...' : 'Change Avatar'}
        </label>
        
        <input
          id="avatar-input"
          type="file"
          accept="image/*"
          onChange={handleFileSelect}
          disabled={uploading}
          style={{ display: 'none' }}
        />

        {preview && preview !== currentAvatar && (
          <button
            onClick={() => setPreview(currentAvatar)}
            className="cancel-button"
            disabled={uploading}
          >
            Cancel
          </button>
        )}
      </div>

      <p className="upload-hint">
        Supported formats: JPG, PNG, GIF. Max size: 5MB.
      </p>
    </div>
  );
}

export default AvatarUpload;
```

### Node.js Backend Handler

```javascript
const express = require('express');
const { body, validationResult } = require('express-validator');
const { authenticateToken } = require('../middleware/auth');

const router = express.Router();

router.put('/user', [
  authenticateToken,
  body('email').optional().isEmail().normalizeEmail(),
  body('phone').optional().matches(/^\+[1-9]\d{1,14}$/),
  body('password').optional().isLength({ min: 8 }),
  body('data').optional().isObject()
], async (req, res) => {
  try {
    // Check validation errors
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({
        code: 400,
        msg: 'Invalid request data',
        details: errors.array()
      });
    }

    const { email, phone, password, data, nonce } = req.body;
    const userId = req.user.sub;

    // Validate password strength if provided
    if (password && !isStrongPassword(password)) {
      return res.status(422).json({
        code: 422,
        msg: 'Password too weak',
        details: 'Password must be at least 8 characters with uppercase, lowercase, number, and special character'
      });
    }

    // Call Strike Auth Service
    const response = await fetch(`${process.env.AUTH_SERVICE_URL}/user`, {
      method: 'PUT',
      headers: {
        'Authorization': req.headers.authorization,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email,
        phone,
        password,
        data,
        nonce
      }),
    });

    const result = await response.json();

    if (!response.ok) {
      return res.status(response.status).json(result);
    }

    // Log profile update
    console.log(`User profile updated: ${userId} (${result.email})`);

    // Send verification emails if email/phone changed
    if (email && email !== req.user.email) {
      console.log(`Email change requested for user ${userId}: ${req.user.email} -> ${email}`);
    }

    if (phone && phone !== req.user.phone) {
      console.log(`Phone change requested for user ${userId}: ${req.user.phone} -> ${phone}`);
    }

    res.json(result);

  } catch (error) {
    console.error('Update user profile error:', error);
    res.status(500).json({
      code: 500,
      msg: 'Internal server error',
      details: 'Failed to update user profile'
    });
  }
});

function isStrongPassword(password) {
  // At least 8 characters, 1 uppercase, 1 lowercase, 1 number, 1 special char
  const strongPasswordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
  return strongPasswordRegex.test(password);
}

module.exports = router;
```

## Verification Requirements

When updating email or phone number, verification is required:

### Email Change Flow

<Steps>
  <Step title="Update Request">
    User submits new email address via PUT /user
  </Step>

  <Step title="Verification Email">
    System sends verification email to new address
  </Step>

  <Step title="Email Confirmation">
    User clicks verification link in email
  </Step>

  <Step title="Email Confirmed">
    New email is confirmed and becomes active
  </Step>
</Steps>

### Phone Change Flow

<Steps>
  <Step title="Update Request">
    User submits new phone number via PUT /user
  </Step>

  <Step title="OTP Sent">
    System sends OTP to new phone number
  </Step>

  <Step title="OTP Verification">
    User enters OTP code for verification
  </Step>

  <Step title="Phone Confirmed">
    New phone number is confirmed and becomes active
  </Step>
</Steps>

## Security Features

* **Authentication Required**: All updates require valid JWT token
* **Email Verification**: New email addresses must be verified
* **Phone Verification**: New phone numbers must be verified
* **Password Strength**: Enforced password complexity requirements
* **Rate Limiting**: Prevents abuse of profile updates
* **Audit Logging**: All profile changes are logged

## Best Practices

<AccordionGroup>
  <Accordion title="Data Validation">
    * Validate all input data on both client and server
    * Sanitize user inputs to prevent XSS attacks
    * Implement proper email and phone format validation
    * Enforce strong password requirements
    * Limit metadata size to prevent abuse
  </Accordion>

  <Accordion title="User Experience">
    * Provide real-time validation feedback
    * Show clear success and error messages
    * Implement auto-save for non-critical fields
    * Allow partial updates without requiring all fields
    * Provide preview functionality for changes
  </Accordion>

  <Accordion title="Security">
    * Always verify email and phone changes
    * Log all profile modifications for audit trails
    * Implement rate limiting for update requests
    * Use HTTPS for all profile update requests
    * Consider implementing change confirmation for sensitive updates
  </Accordion>
</AccordionGroup>

## Testing

### Unit Tests

```javascript
describe('PUT /user', () => {
  test('should update user profile successfully', async () => {
    const token = await getValidAccessToken();
    
    const updateData = {
      data: {
        first_name: 'Updated',
        last_name: 'Name',
        preferences: {
          theme: 'dark'
        }
      }
    };

    const response = await request(app)
      .put('/user')
      .set('Authorization', `Bearer ${token}`)
      .send(updateData)
      .expect(200);

    expect(response.body.user_metadata.first_name).toBe('Updated');
    expect(response.body.user_metadata.last_name).toBe('Name');
    expect(response.body.user_metadata.preferences.theme).toBe('dark');
  });

  test('should require authentication', async () => {
    await request(app)
      .put('/user')
      .send({ data: { first_name: 'Test' } })
      .expect(401);
  });

  test('should validate email format', async () => {
    const token = await getValidAccessToken();
    
    await request(app)
      .put('/user')
      .set('Authorization', `Bearer ${token}`)
      .send({ email: 'invalid-email' })
      .expect(400);
  });

  test('should validate phone format', async () => {
    const token = await getValidAccessToken();
    
    await request(app)
      .put('/user')
      .set('Authorization', `Bearer ${token}`)
      .send({ phone: '1234567890' }) // Missing +
      .expect(400);
  });

  test('should enforce password strength', async () => {
    const token = await getValidAccessToken();
    
    await request(app)
      .put('/user')
      .set('Authorization', `Bearer ${token}`)
      .send({ password: 'weak' })
      .expect(422);
  });
});
```

## Related Endpoints

<CardGroup cols={2}>
  <Card title="Get User Profile" icon="user" href="/api-reference/user/get-profile">
    Retrieve current user profile
  </Card>

  <Card title="Verify Email" icon="envelope-check" href="/api-reference/authentication/verify-post">
    Verify new email address
  </Card>

  <Card title="Verify Phone" icon="phone-check" href="/api-reference/authentication/verify-post">
    Verify new phone number
  </Card>

  <Card title="Change Password" icon="key" href="/api-reference/user/change-password">
    Change user password
  </Card>
</CardGroup>
