Skip to content

Repository files navigation

AuthGuard

A standalone authentication and security REST API project. Provides local email/password authentication, Google OAuth2, session management, and SMTP configuration through a clean, layered architecture.

Tech Stack

Concern Technology
Runtime .NET 8
ORM Entity Framework Core 8 + Npgsql
Database PostgreSQL
Auth JWT Bearer (HttpOnly cookie + Authorization header fallback)
Password hashing BCrypt.Net-Next
Email MailKit
Validation FluentValidation
API docs Swagger / OpenAPI (Swashbuckle)

Architecture

The solution follows Clean Architecture with a strict unidirectional dependency rule:

Common  <--  DTO  <--  Domain  <--  Application  <--  API
                           ^               ^              ^
                     Infrastructure -------+-------------- 
Project Responsibility
AuthGuard.Common Enums, helpers, Optional<T> — zero dependencies
AuthGuard.DTO Positional records for request/response — no Domain references
AuthGuard.Domain Entities, repository interfaces, Result<T>, IUnitOfWork
AuthGuard.Application Service interfaces and implementations, validators, error messages
AuthGuard.Infrastructure EF Core, repositories, JWT, BCrypt, MailKit
AuthGuard.API Controllers, middleware, DI wiring

Prerequisites

Getting Started

1. Clone the repository

git clone https://github.com/lgimenez-dev/auth-guard.git
cd auth-guard

2. Create the database

Create the database and run the init script. It creates the schema, indexes, and seeds the initial admin user (password: 1234):

sudo -u postgres psql -c "CREATE DATABASE authguard;"
sudo -u postgres psql -d authguard -f scripts/init.sql

3. Configure the application

Edit AuthGuard.API/appsettings.Development.json and replace the placeholders. To generate a cryptographically secure value for "Jwt.Secret", just run openssl rand -hex 32

{
  "ConnectionStrings": {
    "Default": "Host=localhost;Port=5432;Database=authguard;Username=YOUR_DB_USER;Password=YOUR_DB_PASSWORD"
  },
  "Jwt": {
    "Secret": "YOUR_JWT_SECRET_MIN_32_CHARACTERS_LONG",
    "ExpirationMinutes": 60
  },
  "Google": {
    "ClientId": "YOUR_GOOGLE_CLIENT_ID",
    "ClientSecret": "YOUR_GOOGLE_CLIENT_SECRET"
  },
  "App": {
    "Name": "AuthGuard",
    "FrontendUrl": "http://localhost:3000"
  }
}

Google credentials are only required if you intend to use the OAuth2 endpoints.

4. Run the API

dotnet run --project AuthGuard.API

Swagger UI is available at https://localhost:{port}/swagger in Development mode.

API Reference

Authentication — POST|GET /api/auth

Method Endpoint Auth Description
POST /api/auth/register Public Creates account (Pending status) and sends activation email
POST /api/auth/login Public Authenticates user, sets access_token HttpOnly cookie. Rate-limited to 5 req/min
POST /api/auth/logout JWT Revokes the current session and deletes the cookie
GET /api/auth/validate-token JWT Confirms the session is active in the database
GET /api/auth/me JWT Returns identity claims from the JWT (no DB hit)
POST /api/auth/recover-password Public Sends a recovery email (always returns 200 to prevent enumeration)
POST /api/auth/change-password Public Sets a new password via one-time token (covers activation and recovery)
GET /api/auth/oauth2/google Public Redirects to Google consent screen

Sessions — /api/sessions

Method Endpoint Auth Description
GET /api/sessions/user/{userId} JWT Lists active sessions for a user
POST /api/sessions/user/{userId}/revoke JWT Revokes all active sessions for a user

SMTP Config — /api/smtp-config

Method Endpoint Auth Description
GET /api/smtp-config JWT (Backoffice) Returns the current SMTP configuration
POST /api/smtp-config JWT (Backoffice) Creates the SMTP configuration (single record)
PUT /api/smtp-config JWT (Backoffice) Replaces all SMTP configuration fields

Authentication Flow

Local registration and activation

POST /register  -->  account created (Pending)  -->  activation email sent
POST /change-password (with token)  -->  account activated  -->  ready to login

Password recovery

POST /recover-password  -->  recovery email sent (if account exists and national ID matches)
POST /change-password (with token)  -->  new password set

Google OAuth2

GET /api/auth/oauth2/google  -->  redirect to Google  -->  callback handled by middleware
  -->  access_token cookie set  -->  redirect to FrontendUrl

On the first Google login, a new account is created as Active (no activation step). If the email already exists under a local account, the login is rejected to prevent account hijacking.

Token Strategy

  • JWTs are issued on login and stored server-side as a SHA-256 hash in user_sessions.
  • The token is read from the access_token HttpOnly cookie first, falling back to the Authorization: Bearer header.
  • GET /api/auth/validate-token verifies the session is still active in the database (not just the JWT signature).
  • Logging in revokes all previous active sessions for the user.

Default Admin Account

The database seed script creates an initial Backoffice user:

Field Value
Email admin@authapp.com
Password 1234
Role Backoffice

Change this password after first login.

Project Structure

AuthGuard/
├── AuthGuard.Common/
│   ├── Enums/                  UserRole, UserStatus, UserProvider
│   ├── Helpers/                NumberHelper, StringHelper
│   └── Optional.cs
├── AuthGuard.DTO/              Positional records for all request/response types
├── AuthGuard.Domain/
│   ├── Entities/               User, Person, UserSession, SmtpConfig
│   ├── Repositories/           IUserRepository, IPersonRepository, ...
│   ├── IUnitOfWork.cs
│   └── Result.cs
├── AuthGuard.Application/
│   ├── Auth/                   IAuthService, AuthService, validators
│   ├── OAuth2/                 IOAuth2Service, OAuth2Service
│   ├── Sessions/               IUserSessionService, UserSessionService
│   ├── SmtpConfig/             ISmtpConfigService, SmtpConfigService, validators
│   ├── Services/               IJwtService, IPasswordService, IEmailService
│   ├── Helpers/                HmacHelper, EmailHelper
│   └── ErrorMessages.cs
├── AuthGuard.Infrastructure/
│   ├── Configurations/         EF Core entity configurations
│   ├── Repositories/           EF Core repository implementations
│   ├── Services/               JwtService, PasswordService, EmailService
│   └── UnitOfWork.cs
└── AuthGuard.API/
    ├── Controllers/            AuthController, UserSessionController, SmtpConfigController
    ├── Extensions/             JwtBearerExtensions, GoogleAuthExtensions, SwaggerExtensions, CorsExtensions
    ├── Middleware/             ExceptionHandlingMiddleware
    ├── Models/                 BusinessErrorResponse, ValidationErrorResponse
    └── Program.cs

Design Decisions

Result pattern over exceptions — services return Result<T> for expected business failures. Exceptions are only thrown for truly unexpected errors and are caught by ExceptionHandlingMiddleware.

No FromEntity on DTOs — mapping from entities to DTOs is done in private static helpers inside each service. DTOs have no dependency on Domain.

Session-based JWT tracking — every issued JWT has a corresponding record in user_sessions. This allows true server-side revocation beyond JWT expiration.

Enum storage — C# enums use PascalCase (Member, Active, Local). EF Core value converters store them as uppercase strings in PostgreSQL (MEMBER, ACTIVE, LOCAL).

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages