TL;DR
This guide shows how to add user authorization to a Windows C# application that doesn’t connect to the internet. We’ll store user details locally (encrypted) and check credentials before allowing access to sensitive features.
Setting up User Storage
- Choose a storage method: For simplicity, we’ll use an encrypted file. More robust options include using the Windows Data Protection API or a lightweight database like SQLite with encryption.
- Create a User class: Define a class to represent user information.
public class User { public string Username { get; set; } public string PasswordHash { get; set; } public string Salt { get; set; } } - Implement Encryption: Use a strong encryption algorithm (e.g., AES) to protect user credentials.
using System.Security.Cryptography; ... private static byte[] Encrypt(string plainText, byte[] key) { byte[] iv = GenerateIV(); AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); aes.Key = key; aes.IV = iv; ICryptoTransform encryptor = aes.CreateEncryptor(); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cs = new CryptoStream(ms, encryptor)) { using (StreamWriter sw = new StreamWriter(cs)) { sw.Write(plainText); } } return ms.ToArray(); } } - Store Users: When a user registers, hash the password (using PBKDF2 or Argon2), generate a salt, and encrypt both the hash and salt before saving them to the file.
// Example of hashing with PBKDF2 using System.Security.Cryptography; ... byte[] salt = GenerateSalt(); string passwordHash = HashPassword(password, salt); User newUser = new User { Username = username, PasswordHash = passwordHash, Salt = Convert.ToBase64String(salt) }; // Encrypt the user object to a byte array before saving.
Authentication Process
- Load Users: When the application starts, decrypt the user file and load the users into memory.
- Login Form: Create a login form with username and password fields.
- Verify Credentials: On login attempt:
- Retrieve the stored user from memory based on the entered username.
- Hash the entered password using the stored salt.
- Compare the hashed entered password with the stored password hash.
Authorization Controls
- Define Roles/Permissions: Determine what access levels your application needs (e.g., Admin, Standard User). You can add a ‘Role’ property to the
Userclass.public enum Role { Admin, StandardUser } public class User { // ... other properties... public Role Role { get; set; } } - Check Permissions: Before allowing access to sensitive features, check the current user’s role.
if (currentUser.Role == Role.Admin) { // Allow access to admin feature } else { // Deny access and show an error message } - UI Control Enablement: Disable or hide UI elements based on the user’s role.
if (currentUser.Role == Role.Admin) { adminButton.Enabled = true; } else { adminButton.Enabled = false; }
Important Considerations
- Salt Generation: Always use a cryptographically secure random number generator for salt generation (e.g.,
RNGCryptoServiceProvider). - Password Hashing Algorithm: Use strong password hashing algorithms like PBKDF2 or Argon2, and configure them with appropriate iteration counts and key lengths.
- Key Management: Securely store the encryption key. Do not hardcode it into the application. Consider using a Windows Credential Store or similar secure storage mechanism.
- Error Handling: Implement robust error handling to prevent information leaks and unexpected behavior.
- Regular Updates: Keep your cryptography libraries up-to-date to benefit from security patches.

