Profile Management
Edit user profiles including photo upload, personal info, and settings in AuthScape.
AuthScape provides comprehensive profile management capabilities for both administrators and end users.
Updating User Profile
Backend Controller
csharp
[Route("api/[controller]")][Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]public class ProfileController : ControllerBase{private readonly UserManager<AppUser> _userManager;private readonly IUserManagementService _userManagementService;[HttpPut("Update")]public async Task<IActionResult> UpdateProfile([FromBody] UpdateProfileRequest request){var signedInUser = _userManagementService.GetSignedInUser();var user = await _userManager.FindByIdAsync(signedInUser.Id.ToString());if (user == null)return NotFound();user.FirstName = request.FirstName;user.LastName = request.LastName;user.PhoneNumber = request.PhoneNumber;user.TimeZoneId = request.TimeZoneId;user.Culture = request.Culture;var result = await _userManager.UpdateAsync(user);if (!result.Succeeded)return BadRequest(result.Errors);return Ok(new { message = "Profile updated" });}}public class UpdateProfileRequest{public string FirstName { get; set; }public string LastName { get; set; }public string? PhoneNumber { get; set; }public string? TimeZoneId { get; set; }public string? Culture { get; set; }}
Frontend Profile Update
javascript
import { apiService } from 'authscape';// Update profileasync function updateProfile(profileData) {const response = await apiService().put('/Profile/Update', {firstName: profileData.firstName,lastName: profileData.lastName,phoneNumber: profileData.phoneNumber,timeZoneId: profileData.timeZoneId,culture: profileData.culture});if (response.status === 200) {console.log('Profile updated successfully');}}
Profile Photo Upload
Using FileUploader Component
jsx
import { FileUploader } from 'authscape/components';import { useState } from 'react';export default function ProfilePhoto({ currentPhoto }) {const [photoUrl, setPhotoUrl] = useState(currentPhoto);return (<div><imgsrc={photoUrl || '/default-avatar.png'}alt="Profile"style={{ width: 100, height: 100, borderRadius: '50%' }}/><FileUploaderurl="/Profile/UploadPhoto"accept="image/*"variant="custom"onUploadCompleted={(result) => {setPhotoUrl(result.photoUri);}}><button>Change Photo</button></FileUploader></div>);}
Backend Photo Upload
csharp
[HttpPost("UploadPhoto")]public async Task<IActionResult> UploadPhoto(IFormFile file){var signedInUser = _userManagementService.GetSignedInUser();var user = await _userManager.FindByIdAsync(signedInUser.Id.ToString());if (user == null)return NotFound();// Upload to Azure Blob Storagevar blobClient = _blobContainerClient.GetBlobClient($"profiles/{user.Id}/{file.FileName}");await blobClient.UploadAsync(file.OpenReadStream(), overwrite: true);// Update user photo URIuser.PhotoUri = blobClient.Uri.ToString();await _userManager.UpdateAsync(user);return Ok(new { photoUri = user.PhotoUri });}
Profile Form Component
jsx
import { useState, useEffect } from 'react';import { TextField, Button, Box, Grid, MenuItem, Select } from '@mui/material';import { apiService } from 'authscape';import { AutoSaveTextField } from 'authscape/components';export default function ProfileForm() {const [profile, setProfile] = useState(null);const [loading, setLoading] = useState(true);useEffect(() => {loadProfile();}, []);async function loadProfile() {const user = await apiService().get('/UserManagement/Get');setProfile(user);setLoading(false);}async function handleSave() {await apiService().put('/Profile/Update', profile);alert('Profile saved!');}if (loading) return <div>Loading...</div>;return (<Box sx={{ p: 3 }}><Grid container spacing={2}><Grid item xs={12} sm={6}><TextFieldlabel="First Name"value={profile.firstName}onChange={(e) => setProfile({ ...profile, firstName: e.target.value })}fullWidth/></Grid><Grid item xs={12} sm={6}><TextFieldlabel="Last Name"value={profile.lastName}onChange={(e) => setProfile({ ...profile, lastName: e.target.value })}fullWidth/></Grid><Grid item xs={12} sm={6}><TextFieldlabel="Email"value={profile.email}disabledfullWidth/></Grid><Grid item xs={12} sm={6}><TextFieldlabel="Phone"value={profile.phoneNumber || ''}onChange={(e) => setProfile({ ...profile, phoneNumber: e.target.value })}fullWidth/></Grid><Grid item xs={12} sm={6}><Selectlabel="Time Zone"value={profile.timeZoneId || ''}onChange={(e) => setProfile({ ...profile, timeZoneId: e.target.value })}fullWidth><MenuItem value="America/New_York">Eastern Time</MenuItem><MenuItem value="America/Chicago">Central Time</MenuItem><MenuItem value="America/Denver">Mountain Time</MenuItem><MenuItem value="America/Los_Angeles">Pacific Time</MenuItem><MenuItem value="UTC">UTC</MenuItem></Select></Grid><Grid item xs={12}><Button variant="contained" onClick={handleSave}>Save Profile</Button></Grid></Grid></Box>);}
Auto-Save Profile Fields
Use AutoSaveTextField for automatic saving:
jsx
import { AutoSaveTextField } from 'authscape/components';import { apiService } from 'authscape';export default function AutoSaveProfile({ userId }) {const handleFieldSave = async (fieldName, value) => {await apiService().put('/Profile/UpdateField', {userId,fieldName,value});};return (<div><AutoSaveTextFieldlabel="Bio"value={user.bio}isMultiLine={true}rows={4}timeout={2000}fullWidthonChanged={(value) => handleFieldSave('bio', value)}/><AutoSaveTextFieldlabel="Job Title"value={user.jobTitle}timeout={1500}fullWidthonChanged={(value) => handleFieldSave('jobTitle', value)}/></div>);}
Change Password
Frontend
javascript
import { apiService } from 'authscape';async function changePassword(currentPassword, newPassword) {const response = await apiService().post('/Profile/ChangePassword', {currentPassword,newPassword});if (response.status === 200) {alert('Password changed successfully');} else {alert('Failed to change password: ' + response.data.message);}}
Backend
csharp
[HttpPost("ChangePassword")]public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordRequest request){var signedInUser = _userManagementService.GetSignedInUser();var user = await _userManager.FindByIdAsync(signedInUser.Id.ToString());if (user == null)return NotFound();var result = await _userManager.ChangePasswordAsync(user,request.CurrentPassword,request.NewPassword);if (!result.Succeeded)return BadRequest(new { message = result.Errors.First().Description });return Ok(new { message = "Password changed successfully" });}
Manage Account (IDP Redirect)
Redirect users to the Identity Provider's account management page:
javascript
import { authService } from 'authscape';// Redirect to IDP account managementfunction manageAccount() {authService().manageAccount();// Redirects to: {authorityUri}/Identity/Account/Manage}
This provides access to:
- Email change
- Password change
- Two-factor authentication setup
- External login providers
- Personal data download
- Account deletion
API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/UserManagement/Get | GET | Get current user profile |
/api/Profile/Update | PUT | Update profile info |
/api/Profile/UploadPhoto | POST | Upload profile photo |
/api/Profile/ChangePassword | POST | Change password |
/api/Profile/UpdateField | PUT | Update single field |
/Identity/Account/Manage | GET | IDP account management |
Best Practices
- Validate input - Server-side validation for all profile updates
- Resize images - Compress and resize profile photos before storage
- Auto-save - Use AutoSaveTextField for long text fields
- Confirm password changes - Require current password
- Audit changes - Log profile modifications for compliance
Next Steps
- Authentication Flows - Login/logout/signup
- Claims & Identity - JWT token claims
- FileUploader - File upload component