Skip to main content
Version: MyBestDealNow

Authentication Management

Author(s)

  • Sanket Mal

Last Updated Date

2025-07-11


SRS References

  • SRS-V1.2

Version History

VersionDateChangesAuthor
1.02025-07-11Initial authentication planSanket 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

  1. Register Dealer/Customer/admin with minimum info, link to profile tables.
  2. Secure login with username/email and password.
  3. Issue JWT access and refresh tokens.
  4. Track login attempts and session lifecycle.
  5. Support password reset and forgot password via email.
  6. Logout and session invalidation.
  7. Store user roles and link to Dealer/Customer entities.
  8. 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:

    EndpointMethodPayloadResponseStatus Codes
    /api/customer/registerPOSTUserMinimalInfoLoginResponse201, 400, 500
    /api/iam/customer-loginPOSTLoginRequestLoginResponse200, 401, 500
    /api/iam/dealer-loginPOSTLoginRequestLoginResponse200, 401, 500
    /api/iam/admin-loginPOSTLoginRequestLoginResponse200, 401, 500
    /api/iam/token/refreshGETDeviceType deviceTypeLoginResponse200, 401, 500
    /api/iam/forgot-passwordPOSTForgotPasswordRequestCommonResponse200, 404, 500
    /api/iam/reset-passwordPUTResetPasswordRequestCommonResponse200, 400, 500
    /api/iam/change-passwordPUTChangePasswordRequestCommonResponse200, 400, 500
    /api/iam/logoutPOST200, 400, 500
    /api/dealer/createPOSTDealerCommonResponse200, 400, 500
  • Third-Party Integrations:

    • Email service for password reset
  • Workflow:

    1. Register: Create tbluser, link to DealerMaster/CustomerMaster, assign role.
    2. Login: Validate credentials, create session, log history, return tokens.
    3. Refresh Token: Validate and renew tokens.
    4. Forgot/Reset Password: Email-based password reset.
    5. Logout: End session, update session and history.
  • Default Role & Scope Allocation Workflow

    When a new user is created:

    1. A default role is created for the user based on their userType (e.g., Dealer, Admin, Customer).
    2. 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.
    3. 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.

Authentication Service DB Diagram

  • 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

NoTask NameEstimate (Hours)DependenciesNotes
1Planning for API & DB Design6NoneArchitecture, workflow, data models, endpoints
2Implement and Test /api/customer/register API5Register customer user, link entity, assign role
3Implement and Test /api/iam/customer-login API4Validate customer user credentials, create session
4Implement and Test /api/iam/token/refresh API3JWT LibraryToken renewal logic
5Implement and Test /api/iam/forgot-password API2Email ServiceSend reset link via email
6Implement and Test /api/iam/reset-password API3Email ServiceReset password using hash
7Implement and Test /api/iam/change-password API3Change password for logged-in user
8Implement and Test /api/iam/logout API2End session, update history
9Implement Notification Service2Email ServiceFor password reset, alerts, etc.
10Create Mail Template for Reset Password1Notification ServiceHTML body for reset password email
11Documentation & API Spec3All APIsSwagger, Postman, markdown docs
12Implement and Test /api/iam/dealer-login API3Validate credentials of dealer user, create session
13Implement and Test /api/iam/admin-login API3Validate credentials of admin user, create session
14Implement and Test /api/dealer/create API5Create new dealer
15Total45

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

RiskImpactLikelihoodMitigation 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.