Authentication Management
Author(s)
- Sanket Mal
Last Updated Date
2025-07-11
SRS References
- SRS-V1.2
Version History
| Version | Date | Changes | Author |
|---|---|---|---|
| 1.0 | 2025-07-11 | Initial authentication plan | Sanket Mal |
Feature Overview
Objective:
Implement secure, scalable authentication management for Dealer, Customer, and admin users, supporting registration, login, session management, password reset, and token refresh.
Scope:
Covers standard username/password authentication for Dealer, Customer, and admin users. Excludes external auth providers and custom OAuth for now.
Dependencies:
- PostgreSQL
- .NET 8 (C#)
- JWT library
- Email service for password reset
Requirements
- Register Dealer/Customer/admin with minimum info, link to profile tables.
- Secure login with username/email and password.
- Issue JWT access and refresh tokens.
- Track login attempts and session lifecycle.
- Support password reset and forgot password via email.
- Logout and session invalidation.
- Store user roles and link to Dealer/Customer entities.
- All additional profile info is optional at registration.
Design Specifications
-
UI/UX Design:
(Handled in frontend, not covered here.) -
Data Models:
(C# models for payloads and responses)public record UserMinimalInfo
{
[Required(ErrorMessage = "First name is required")]
public required string FirstName { get; init; }
[Required(ErrorMessage = "Last name is required")]
public required string LastName { get; init; }
[EmailAddress(ErrorMessage = "Invalid email address")]
public string? Email { get; init; }
[Phone(ErrorMessage = "Invalid phone number")]
public string? Phone { get; init; }
[Required(ErrorMessage = "Password is required")]
[DataType(DataType.Password)]
public required string Password { get; set; }
}
public record LoginResponse
{
public string? AccessToken { get; set; }
public double? ExpiresIn { get; set; }
public string? TokenType { get; set; } = "Bearer";
}
public record LoginRequest
{
public string? Email { get; init; }
public string? Phone { get; init; }
[Required(ErrorMessage = "Password is required")]
[DataType(DataType.Password)]
public required string Password { get; init; }
}
public record CommonResponse
{
public int Status { get; init; }
public string? Message { get; init; }
}
public record ChangePasswordRequest
{
[Required(ErrorMessage = "Old Password is required")]
[DataType(DataType.Password)]
public string OldPassword { get; init; }
[Required(ErrorMessage = "New Password is required")]
[DataType(DataType.Password)]
public required string NewPassword { get; init; }
}
public record ResetPasswordRequest
{
public required string RequestHash {get; set;}
[Required(ErrorMessage = "New Password is required")]
[DataType(DataType.Password)]
public required string NewPassword { get; init; }
}
public record ForgotPasswordRequest
{
public string? Email { get; init; }
public string? Phone { get; init; }
public required string CallbackUrl { get; init; }
}
public record Scope
{
public Guid ScopeId { get; init; }
public required string ScopeName { get; init; }
public int AccessType { get; init; }
public required string DisplayName { get; init; }
public required string GroupName { get; init; }
public int GroupSortOrder { get; init; }
public int ScopeSortOrder { get; init; }
public string? Description {get; init;}
}
public record Role
{
public Guid RoleId { get; init; }
public required string RoleName { get; init; }
public string? Description { get; init; }
public UserType UserType { get; init; }
public Guid EntityId { get; init; }
public bool IsActive { get; init; }
public string? LogUsername { get; set; }
public DateTime LogDts { get; set; } = DateTime.UtcNow;
}
// For create/update requests
public record RoleWithScopeIds : Role
{
public List<Guid> ScopeIds { get; set; } = new();
}
// For get/details response
public record RoleDetails : Role
{
public List<Scope> Scopes { get; set; } = new();
}
public record Dealer
{
public Guid DealerId { get; set; }
public required string PrimaryEmail { get; init; }
public string? CompanyName { get; init; }
public string? ContactPersonName { get; init; }
public string? Phone { get; init; }
public string? Website { get; init; }
public List<DealershipType>? DealershipType { get; init; }
public List<Brand>[]? Brand { get; init; }
public Status Status { get; set; } = Status.Active;
public string? DealerLogoUrl { get; set; }
public List<DealerAddress>? Addresses { get; init; } = new List<DealerAddress>();
public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow;
public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;
public string? LogUsername { get; set; }
public DateTimeOffset LogDts { get; set; } = DateTimeOffset.UtcNow;
}
public record DealerAddress
{
public Guid AddressId { get; init; } // Primary key
public Guid DealerId { get; init; } // Foreign key
public AddressType AddressType { get; init; }
public string? AddressLine { get; init; }
public string? State { get; init; }
public string? Zipcode { get; init; }
public string? Country { get; init; }
}
public enum UserType
{
Admin = 1,
Dealer = 2,
Customer = 4
}
public enum UserStatus
{
Active = 1,
Disable = 2,
Uninitialized = 3
}
public enum Gender
{
Male = 1,
Female = 2,
Other = 3
}
public enum Status
{
Active = 1,
Disable = 2
}
public enum DeviceType
{
Web = 1,
Mobile = 2
}
public enum DealershipType
{
New = 1,
PreOwned = 2
}
public enum AddressType
{
Legal = 1, // Registered/legal address of the business
Billing = 2, // Address used for invoicing
Shipping = 3 // Address where goods are delivered
}
public enum Brand
{
Acura = 1,
AlfaRomeo,
AstonMartin,
Audi,
Bentley,
BMW,
Bugatti,
Buick,
Cadillac,
Chevrolet,
Chrysler,
Dodge,
Ferrari,
Fiat,
Fisker,
Ford,
Genesis,
GMC,
Honda,
Hyundai,
Infiniti,
Jaguar,
Jeep,
Kia,
Lamborghini,
LandRover,
Lexus,
Lincoln,
Lotus,
Maserati,
Mazda,
McLaren,
MercedesBenz,
Mini,
Mitsubishi,
Nissan,
Porsche,
Ram,
Rivian,
RollsRoyce,
Saab,
Saturn,
Scion,
Subaru,
Tesla,
Toyota,
Volkswagen,
Volvo
} -
API Interfaces:
Endpoint Method Payload Response Status Codes /api/customer/registerPOST UserMinimalInfoLoginResponse201, 400, 500 /api/iam/customer-loginPOST LoginRequestLoginResponse200, 401, 500 /api/iam/dealer-loginPOST LoginRequestLoginResponse200, 401, 500 /api/iam/admin-loginPOST LoginRequestLoginResponse200, 401, 500 /api/iam/token/refreshGET DeviceType deviceTypeLoginResponse200, 401, 500 /api/iam/forgot-passwordPOST ForgotPasswordRequestCommonResponse200, 404, 500 /api/iam/reset-passwordPUT ResetPasswordRequestCommonResponse200, 400, 500 /api/iam/change-passwordPUT ChangePasswordRequestCommonResponse200, 400, 500 /api/iam/logoutPOST 200, 400, 500 /api/dealer/createPOST DealerCommonResponse200, 400, 500 -
Third-Party Integrations:
- Email service for password reset
-
Workflow:
- Register: Create tbluser, link to DealerMaster/CustomerMaster, assign role.
- Login: Validate credentials, create session, log history, return tokens.
- Refresh Token: Validate and renew tokens.
- Forgot/Reset Password: Email-based password reset.
- Logout: End session, update session and history.
-
Default Role & Scope Allocation Workflow
When a new user is created:
- A default role is created for the user based on their userType (e.g., Dealer, Admin, Customer).
- All scopes that are accessible for this userType (as defined by the accessType bitmask in the scope table) are allocated to this role using the rolescopemapping table.
- The user is assigned this default role.
-
Steps:
- Determine userType at registration (Dealer, Admin, Customer).
- Create a new role record for the user (e.g., 'Default Dealer Role').
- Query the scope table for all scopes where (accessType & userType) = userType.
- For each matching scope, create a rolescopemapping entry linking the new role and the scope.
- Assign the new role to the user in the user table.
This ensures every user starts with a role that has all permissions relevant to their type, and future changes to scopes or roles can be managed centrally.
Database Tables & Links (PostgreSQL)

- userdetails
CREATE TABLE userdetails (
userId UUID PRIMARY KEY,
firstname VARCHAR(100),
lastname VARCHAR(100),
email VARCHAR(100) UNIQUE,
phone VARCHAR(20) UNIQUE,
password VARCHAR(255) NOT NULL,
status VARCHAR(20) NOT NULL,
userType VARCHAR(20) NOT NULL, -- 'Dealer', 'Customer'
entityId UUID NOT NULL, -- dealer_id or customer_id
roleId UUID NOT NULL,
createdAt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
-- Add email or phone check constraint
ALTER TABLE userdetails
ADD CONSTRAINT chk_email_or_phone
CHECK (
email IS NOT NULL OR phone IS NOT NULL
);
ALTER TABLE userdetails
ADD CONSTRAINT fk_role
FOREIGN KEY (roleId) REFERENCES role(roleId);
ALTER TABLE userdetails
ADD CONSTRAINT chk_entityid_usertype
CHECK (
CASE
WHEN userType IN ('Dealer', 'Admin') THEN entityId IN (SELECT dealerId FROM dealermaster)
WHEN userType = 'Customer' THEN entityId IN (SELECT customerId FROM customermaster)
ELSE FALSE
END
);
- dealermaster
CREATE TABLE dealermaster (
dealerId UUID PRIMARY KEY,
primaryemail VARCHAR(100) NOT NULL,
companyName VARCHAR(100),
contactPersonName VARCHAR(100),
phone VARCHAR(20),
website VARCHAR(100),
dealershipType TEXT[], -- Array of dealership types
brand TEXT[], -- Array of brands (makes)
status VARCHAR(20) DEFAULT 'Active',
dealerLogoUrl VARCHAR,
createdAt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
logUsername VARCHAR(50),
logDts TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
- dealeraddress
CREATE TABLE dealeraddress (
addressId UUID PRIMARY KEY,
dealerId UUID NOT NULL REFERENCES dealermaster(dealerId),
addressType VARCHAR(50) NOT NULL,
addressline VARCHAR(100),
state VARCHAR,
zipcode VARCHAR,
country VARCHAR
);
- customermaster
CREATE TABLE customermaster (
customerId UUID PRIMARY KEY,
firstname VARCHAR(100),
lastname VARCHAR(100),
email VARCHAR(100),
phone VARCHAR(20),
dateOfBirth DATE,
gender VARCHAR(10),
profileImageUrl VARCHAR(255),
status VARCHAR(20) DEFAULT 'Active',
createdAt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
logUsername VARCHAR(50),
logDts TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
- loginhistory
CREATE TABLE loginhistory (
loginId UUID PRIMARY KEY,
userId UUID NOT NULL,
attemptTime TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
loginTime TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
logoutTime TIMESTAMPTZ,
isSuccess BOOLEAN NOT NULL,
ipAddress VARCHAR(45),
deviceType TEXT,
useragent TEXT
);
-- Add to loginhistory table
ALTER TABLE loginhistory
ADD CONSTRAINT fk_user
FOREIGN KEY (userId) REFERENCES userdetails(userId);
- session history
CREATE TABLE sessionhistory (
sessionid UUID PRIMARY KEY,
userid UUID NOT NULL,
loginid UUID NOT NULL,
refreshtoken TEXT NOT NULL, -- refresh token for renewing access
devicetype TEXT,
useragent TEXT,
ipaddress VARCHAR(45),
logintime TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
lastrefreshedat TIMESTAMPTZ,
logouttime TIMESTAMPTZ,
expiresat TIMESTAMPTZ,
isactive BOOLEAN DEFAULT TRUE,
FOREIGN KEY (userId) REFERENCES userdetails(userId),
FOREIGN KEY (loginId) REFERENCES loginhistory(loginId)
);
-- Add to sessions table
ALTER TABLE sessions
ADD CONSTRAINT fk_user_session
FOREIGN KEY (userId) REFERENCES userdetails(userId);
ALTER TABLE sessions
ADD CONSTRAINT fk_login_session
FOREIGN KEY (loginid) REFERENCES loginhistory(loginid);
- active sessions
CREATE TABLE activesessions (
sessionId UUID PRIMARY KEY,
userId UUID NOT NULL,
loginId UUID NOT NULL,
refreshtoken TEXT NOT NULL,
deviceType TEXT,
userAgent TEXT,
ipAddress VARCHAR(45),
loginTime TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
lastRefreshedAt TIMESTAMPTZ,
expiresAt TIMESTAMPTZ,
FOREIGN KEY (userId) REFERENCES userdetails(userId),
FOREIGN KEY (loginId) REFERENCES loginhistory(loginId)
);
- role
CREATE TABLE role (
roleId UUID PRIMARY KEY,
rolename VARCHAR(50) NOT NULL,
description VARCHAR(255),
userType VARCHAR(20) NOT NULL,
entityId UUID NOT NULL,
logUsername VARCHAR(50),
logDts TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
- scope
CREATE TABLE scope (
scopeId UUID PRIMARY KEY,
scopeName VARCHAR(100) NOT NULL,
accessType INT NOT NULL,
displayName VARCHAR(100) NOT NULL,
groupName VARCHAR(100) NOT NULL,
groupSortOrder INT,
scopeSortOrder INT,
description VARCHAR(100)
);
- rolescopemapping
CREATE TABLE rolescopemapping (
mappingId UUID PRIMARY KEY,
roleId UUID NOT NULL,
scopeId UUID NOT NULL,
createdAt TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (roleId) REFERENCES role(roleId),
FOREIGN KEY (scopeId) REFERENCES scope(scopeId)
);
Links:
- userdetails.entityid → dealermaster.dealerid or customermaster.customerid
- userdetails.roleid → role.roleid
- loginhistory.userid → userdetails.userid
- sessions.userid → userdetails.userid
- sessions.loginid → loginhistory.loginid
Development Tasks & Estimates
| No | Task Name | Estimate (Hours) | Dependencies | Notes |
|---|---|---|---|---|
| 1 | Planning for API & DB Design | 6 | None | Architecture, workflow, data models, endpoints |
| 2 | Implement and Test /api/customer/register API | 5 | Register customer user, link entity, assign role | |
| 3 | Implement and Test /api/iam/customer-login API | 4 | Validate customer user credentials, create session | |
| 4 | Implement and Test /api/iam/token/refresh API | 3 | JWT Library | Token renewal logic |
| 5 | Implement and Test /api/iam/forgot-password API | 2 | Email Service | Send reset link via email |
| 6 | Implement and Test /api/iam/reset-password API | 3 | Email Service | Reset password using hash |
| 7 | Implement and Test /api/iam/change-password API | 3 | Change password for logged-in user | |
| 8 | Implement and Test /api/iam/logout API | 2 | End session, update history | |
| 9 | Implement Notification Service | 2 | Email Service | For password reset, alerts, etc. |
| 10 | Create Mail Template for Reset Password | 1 | Notification Service | HTML body for reset password email |
| 11 | Documentation & API Spec | 3 | All APIs | Swagger, Postman, markdown docs |
| 12 | Implement and Test /api/iam/dealer-login API | 3 | Validate credentials of dealer user, create session | |
| 13 | Implement and Test /api/iam/admin-login API | 3 | Validate credentials of admin user, create session | |
| 14 | Implement and Test /api/dealer/create API | 5 | Create new dealer | |
| 15 | Total | 45 |
Testing & Quality Assurance
- Unit Tests:
- Register, Login, Refresh Token, Change Password and Reset Password APIs
- Using xUnit
- Integration Tests:
- End-to-end user flows
- Acceptance Criteria:
- All APIs work as specified, secure, and meet requirements
- Testing Tools:
- Postman, Swagger
Deployment Considerations
- Configuration Changes:
- JWT secret, email service config
- Rollout Plan:
- Deploy to staging, run tests, then production
Risks & Mitigations
| Risk | Impact | Likelihood | Mitigation Strategy |
|---|---|---|---|
| ... | ... | ... | ... |
Review & Approval
- Reviewer: Abhishak Kumar Roy
- Approval Date: 2025-07-12
Notes
All profile fields in DealerMaster and CustomerMaster are optional at registration and can be updated later. Authentication is designed for extensibility and security.