✨ New: Multi-Tenant Architecture v2.0

Secure Your Apps with DoorAuth

Open-source SSO, OAuth 2.0, and OIDC server designed for modern security and seamless multi-tenant integration.

40+
API Endpoints
100%
Test Coverage
3
Working Examples
MIT
Open Source

How DoorAuth Works

Understanding the authentication flow and architecture

đŸ—ī¸ System Architecture

đŸšĒ
DoorAuth Server
Central Identity Provider
OAuth 2.0/OIDC Multi-Tenant RBAC Engine
Secure Authentication
⚡
Blazor App
:7231
🌐
ASP.NET App
:7140
âš›ī¸
React SPA
:5175
📱
Your Apps
Any

🔄 Authentication Flow

1
User Visits App
User accesses your application
↓
2
Redirect to DoorAuth
App redirects to DoorAuth login
↓
3
User Authenticates
Credentials + 2FA (optional)
↓
4
Generate Token
DoorAuth creates JWT token
↓
5
Return to App
User authenticated & authorized

Problems We Solve

Common authentication challenges, solved out of the box

❌

Without DoorAuth

  • Duplicate authentication code in every app
  • Inconsistent security practices
  • Multiple user databases to manage
  • Complex password reset flows
  • Manual session management
→
✅

With DoorAuth

  • Centralized authentication for all apps
  • Enterprise-grade security by default
  • Single user database, complete isolation
  • Built-in password recovery
  • Automatic session handling
đŸĸ

SaaS Applications

Challenge: Managing multiple tenants with isolated data

Solution: Built-in multi-tenancy with complete data isolation, tenant-specific roles, and custom branding per tenant.

🔐

Enterprise SSO

Challenge: Users need to login to multiple internal apps

Solution: Single Sign-On across all applications. Login once, access everything. Single logout clears all sessions.

đŸŽ¯

Complex Permissions

Challenge: Different users need different access levels

Solution: Granular RBAC system with role hierarchies, permission-based menu filtering, and API-level authorization.

🚀

Rapid Development

Challenge: Building auth from scratch takes weeks

Solution: Deploy DoorAuth in minutes. Integrate with your app in under an hour. Focus on your business logic, not auth.

🌟 Special Features

Unique capabilities that set DoorAuth apart

⭐ Unique
đŸŽ¯

Smart Menu Generation

Dynamic, permission-based navigation

What is it?

DoorAuth automatically generates navigation menus based on user permissions. Each user sees only the menu items they're authorized to access.

How it works:

1 Define menu structure in DoorAuth
2 Assign permissions to menu items
3 Link permissions to roles
4 API returns filtered menu for user

Example API Response:

GET /api/menus/smart
{
  "menus": [
    {
      "id": 1,
      "label": "Dashboard",
      "icon": "📊",
      "route": "/dashboard",
      "order": 1
    },
    {
      "id": 2,
      "label": "Users",
      "icon": "đŸ‘Ĩ",
      "route": "/users",
      "order": 2,
      "children": [
        { "label": "View Users", "route": "/users/list" },
        { "label": "Add User", "route": "/users/add" }
      ]
    }
  ]
}

Benefits:

  • ✅ No hardcoded menus in your app
  • ✅ Automatic UI updates when permissions change
  • ✅ Consistent navigation across all apps
  • ✅ Hierarchical menu support
  • ✅ Multi-application menu management
⭐ Unique
đŸĸ

Advanced Tenant Management

Enterprise-grade multi-tenancy

What is it?

Complete tenant isolation system that allows you to serve multiple customers (tenants) from a single DoorAuth instance with guaranteed data separation.

Key Capabilities:

🔒
Complete Data Isolation

Each tenant's data is completely separated. Users can only access their tenant's data.

đŸ‘Ĩ
Tenant-Specific Roles

Define different roles and permissions for each tenant independently.

🎨
Custom Branding

Each tenant can have custom branding, logos, and themes.

📊
Tenant Analytics

Track usage, users, and activity per tenant.

Tenant Hierarchy:

DoorAuth Instance
Tenant A
Users: 150
Apps: 5
Roles: 8
Tenant B
Users: 300
Apps: 3
Roles: 12
Tenant C
Users: 75
Apps: 2
Roles: 5

Perfect for:

  • ✅ SaaS applications with multiple customers
  • ✅ Enterprise deployments with divisions
  • ✅ White-label solutions
  • ✅ Multi-organization platforms
  • ✅ Reseller/partner programs
🔄

Token Refresh & Rotation

Automatic token refresh with rotation for enhanced security. Refresh tokens expire after 7 days and rotate on each use.

📧

Email Verification

Built-in email verification system with customizable templates. Secure token-based verification with expiration.

đŸ›Ąī¸

Brute Force Protection

Automatic account locking after failed login attempts. Configurable thresholds and lockout duration.

📱

TOTP 2FA

Time-based One-Time Password authentication. Compatible with Google Authenticator, Authy, and other TOTP apps.

Key Features

Enterprise-grade security features out of the box

🔐

OAuth 2.0 / OIDC

Full OpenID Connect provider with PKCE security, authorization code flow, and refresh tokens.

  • ✓ Authorization Code Flow
  • ✓ PKCE Security
  • ✓ Refresh Token Rotation
  • ✓ JWT Token Management
đŸĸ

Multi-Tenancy

Complete tenant isolation with dedicated data spaces. Perfect for SaaS applications.

  • ✓ Complete Data Isolation
  • ✓ Tenant-Specific Roles
  • ✓ Custom Branding
  • ✓ Scalable Architecture
đŸŽ¯

RBAC System

Role-based access control with granular permissions and dynamic menu generation.

  • ✓ Granular Permissions
  • ✓ Role Hierarchies
  • ✓ Smart Menu Filtering
  • ✓ API-Level Authorization
🔄

Single Sign-On

Login once, access all applications. Centralized authentication with SSO logout.

  • ✓ Cross-App Authentication
  • ✓ Single Sign-Out
  • ✓ Session Management
  • ✓ Remember Me
đŸ›Ąī¸

Security First

Enterprise-grade security with 2FA, brute force protection, and account locking.

  • ✓ Two-Factor Auth (TOTP)
  • ✓ Brute Force Protection
  • ✓ Password Recovery
  • ✓ Email Verification
📊

Admin Dashboard

Beautiful React-based admin panel for managing users, tenants, apps, and roles.

  • ✓ User Management
  • ✓ Tenant Management
  • ✓ Application Registry
  • ✓ Role & Permission Editor

Quick Start

Get your authentication server running in less than 5 minutes

1

Clone the Repository

Get the source code from GitHub

git clone https://github.com/alaminmain/DoorAuthServer.git
cd DoorAuthServer
2

Install Dependencies

Install server and client packages

# Install server dependencies
cd server
npm install

# Install client dependencies
cd ../client
npm install
3

Setup Database

Initialize PostgreSQL database with Prisma

cd server
npx prisma migrate dev
npx prisma db seed
4

Start the Server

Run the development server

# Start backend
npm run dev

# In another terminal, start frontend
cd ../client
npm run dev

Access Points

API Server https://localhost:3000
Swagger UI https://localhost:3000/api-docs
Admin Dashboard https://localhost:3000
Prisma Studio npx prisma studio

Default Credentials

Email: bd@gmail.com
Password: 1q2w3E*
Tenant: Default (ID: 1)

Interactive Integration Guide

Step-by-step integration for your application framework

Step 1

Install NuGet Packages

Add OpenID Connect authentication packages to your Blazor project

dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect --version 9.0.0
dotnet add package Microsoft.AspNetCore.Authentication.Cookies --version 2.2.0
Step 2

Register Application in DoorAuth

Add your application to the DoorAuth database

// In server/prisma/seed.ts, add:
{
    clientId: 'your-app-client-id',
    clientSecret: 'your-app-secret-key',
    name: 'Your Application Name',
    appUrl: 'https://localhost:7231',
    redirectUris: 'https://localhost:7231/signin-oidc',
    grantTypes: 'authorization_code,refresh_token',
    scopes: 'openid,profile,email',
    tenantId: 1,
    isActive: true
}
Step 3

Configure Program.cs

Add OIDC authentication to your application

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
    options.Authority = "https://localhost:3000";
    options.ClientId = "your-app-client-id";
    options.ClientSecret = "your-app-secret-key";
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;
    options.UsePkce = true;
});
Step 4

Add Authentication Middleware

Enable authentication in your application pipeline

app.UseAuthentication();
app.UseAuthorization();

// Login endpoint
app.MapGet("/login", () => Results.Challenge(
    new AuthenticationProperties { RedirectUri = "/" },
    new[] { OpenIdConnectDefaults.AuthenticationScheme }
));

// Logout endpoint
app.MapGet("/logout", async (HttpContext context) =>
{
    await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    await context.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
});
Step 5

Protect Your Pages

Add authorization to your Razor components

@page "/dashboard"
@attribute [Authorize]

<h1>Welcome, @context.User.Identity?.Name</h1>

<AuthorizeView>
    <Authorized>
        <p>You are logged in!</p>
    </Authorized>
    <NotAuthorized>
        <a href="/login">Please login</a>
    </NotAuthorized>
</AuthorizeView>
Step 6

Test Your Integration

Step 1

Create ASP.NET Core Project

Create a new ASP.NET Core Razor Pages or MVC application

# Create new Razor Pages project
dotnet new webapp -n MyApp
cd MyApp

# Add OIDC authentication package
dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect
Step 2

Register Application in DoorAuth

Add your application to DoorAuth via Admin Dashboard or database seed

// In server/prisma/seed.ts, add:
{
    clientId: 'door-auth-sample',
    clientSecret: 'sample-secret-key',
    name: 'DoorAuth Portal',
    appUrl: 'https://localhost:7140',
    redirectUris: 'https://localhost:7140/signin-oidc',
    grantTypes: 'authorization_code,refresh_token',
    scopes: 'openid,profile,email',
    tenantId: 1,
    isActive: true
}
Step 3

Configure OIDC in Program.cs

Set up OpenID Connect authentication with DoorAuth endpoints

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

var builder = WebApplication.CreateBuilder(args);

// Configure Cookie settings for cross-site SSO
builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.MinimumSameSitePolicy = SameSiteMode.None;
    options.Secure = CookieSecurePolicy.Always;
});

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
    options.Cookie.SameSite = SameSiteMode.None;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
})
.AddOpenIdConnect(options =>
{
    options.ClientId = "door-auth-sample";
    options.ClientSecret = "sample-secret-key";
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;
    options.UsePkce = true;
    options.GetClaimsFromUserInfoEndpoint = true;

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("email");

    // Configure DoorAuth OIDC endpoints
    options.Configuration = new OpenIdConnectConfiguration
    {
        Issuer = "https://localhost:3000",
        AuthorizationEndpoint = "https://localhost:3000/api/oauth/authorize",
        TokenEndpoint = "https://localhost:3000/api/oauth/token",
        UserInfoEndpoint = "https://localhost:3000/api/oauth/userinfo",
        EndSessionEndpoint = "https://localhost:3000/api/oauth/end_session",
        JwksUri = "https://localhost:3000/.well-known/jwks.json"
    };
});
Step 4

Add Authentication Middleware

Enable authentication and authorization in the request pipeline

var app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();

app.Run();
Step 5

Create Login/Logout Endpoints

Add endpoints for triggering authentication challenges and sign-out

// Login endpoint - triggers OIDC flow
app.MapGet("/login", () => Results.Challenge(
    new AuthenticationProperties { RedirectUri = "/" },
    new[] { OpenIdConnectDefaults.AuthenticationScheme }
));

// Logout endpoint - signs out from both app and DoorAuth
app.MapGet("/logout", async (HttpContext context) =>
{
    await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    await context.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme,
        new AuthenticationProperties { RedirectUri = "/" });
});
Step 6

Access User Info & Tokens

Retrieve authenticated user claims and access token for API calls

// In a Razor Page or Controller
public class IndexModel : PageModel
{
    public string UserName { get; set; }
    public string AccessToken { get; set; }

    public async Task OnGetAsync()
    {
        if (User.Identity?.IsAuthenticated == true)
        {
            // Get user claims
            UserName = User.Identity.Name;
            var email = User.FindFirst("email")?.Value;
            var tenantId = User.FindFirst("tenantId")?.Value;

            // Get access token for API calls
            AccessToken = await HttpContext.GetTokenAsync("access_token");

            // Call DoorAuth API with token
            using var client = new HttpClient();
            client.DefaultRequestHeaders.Authorization =
                new AuthenticationHeaderValue("Bearer", AccessToken);

            var response = await client.GetAsync(
                "https://localhost:3000/api/users/me/applications");
        }
    }
}
Step 7

Create Protected Pages

Add authorization to your Razor pages

// Pages/Dashboard.cshtml.cs
[Authorize]
public class DashboardModel : PageModel
{
    public void OnGet()
    {
        // Only authenticated users can access this page
    }
}

// Pages/Dashboard.cshtml
@page
@model DashboardModel
@{
    ViewData["Title"] = "Dashboard";
}

<h1>Welcome, @User.Identity?.Name!</h1>

@if (User.Identity?.IsAuthenticated == true)
{
    <p>Email: @User.FindFirst("email")?.Value</p>
    <a href="/logout">Logout</a>
}
else
{
    <a href="/login">Login with DoorAuth</a>
}
Step 8

Test Your Integration

📚 Working Example: DoorAuthSample

See the complete SSO Portal implementation in the DoorAuthSample project. It demonstrates fetching user applications and launching them with SSO tokens.

Step 1

Create React Project & Install Dependencies

Set up a new React project with Vite and install required packages

# Create new React + TypeScript project
npm create vite@latest my-app -- --template react-ts
cd my-app

# Install required dependencies
npm install axios react-router-dom jwt-decode
Step 2

Register Application in DoorAuth

Add your React SPA to DoorAuth via Admin Dashboard or database

// In server/prisma/seed.ts, add:
{
    clientId: 'todo-app-client',
    clientSecret: 'todo-secret-key',
    name: 'Todo App',
    appUrl: 'http://localhost:5175',
    redirectUris: 'http://localhost:5175/callback',
    grantTypes: 'authorization_code,refresh_token',
    scopes: 'openid,profile,email',
    tenantId: 1,
    isActive: true
}
Step 3

Create Auth Configuration

Define OAuth configuration settings for DoorAuth

// src/auth/authConfig.ts
export const authConfig = {
    authority: 'https://localhost:3000',      // DoorAuth server
    clientId: 'todo-app-client',              // Your client ID
    redirectUri: 'http://localhost:5175/callback',
    responseType: 'code',                     // Authorization code flow
    scope: 'openid profile email',            // Requested scopes

    // OAuth endpoints
    authorizationEndpoint: '/api/oauth/authorize',
    tokenEndpoint: '/api/oauth/token',
    userInfoEndpoint: '/api/oauth/userinfo',
    endSessionEndpoint: '/api/oauth/end_session'
};
Step 4

Implement PKCE Auth Service

Create the authentication service with PKCE security (prevents code interception attacks)

// src/services/AuthService.ts
import { authConfig } from '../auth/authConfig';

class AuthService {
    // Generate cryptographically secure PKCE verifier
    private generateCodeVerifier(): string {
        const array = new Uint8Array(32);
        crypto.getRandomValues(array);
        return Array.from(array, byte =>
            byte.toString(16).padStart(2, '0')).join('');
    }

    // Create SHA-256 challenge from verifier
    private async generateCodeChallenge(verifier: string): Promise<string> {
        const encoder = new TextEncoder();
        const data = encoder.encode(verifier);
        const digest = await crypto.subtle.digest('SHA-256', data);
        return btoa(String.fromCharCode(...new Uint8Array(digest)))
            .replace(/\+/g, '-')
            .replace(/\//g, '_')
            .replace(/=+$/, '');
    }

    // Initiate OAuth login with PKCE
    async login(): Promise<void> {
        const verifier = this.generateCodeVerifier();
        const challenge = await this.generateCodeChallenge(verifier);

        // Store verifier for token exchange
        localStorage.setItem('pkce_verifier', verifier);

        // Build authorization URL
        const params = new URLSearchParams({
            client_id: authConfig.clientId,
            redirect_uri: authConfig.redirectUri,
            response_type: authConfig.responseType,
            scope: authConfig.scope,
            code_challenge: challenge,
            code_challenge_method: 'S256'
        });

        // Redirect to DoorAuth
        window.location.href =
            `${authConfig.authority}${authConfig.authorizationEndpoint}?${params}`;
    }

    // Exchange authorization code for tokens
    async handleCallback(code: string): Promise<string> {
        const verifier = localStorage.getItem('pkce_verifier');
        if (!verifier) throw new Error('No PKCE verifier found');

        const response = await fetch(
            `${authConfig.authority}${authConfig.tokenEndpoint}`,
            {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: new URLSearchParams({
                    grant_type: 'authorization_code',
                    code,
                    redirect_uri: authConfig.redirectUri,
                    client_id: authConfig.clientId,
                    code_verifier: verifier
                })
            }
        );

        const data = await response.json();
        localStorage.removeItem('pkce_verifier');
        localStorage.setItem('access_token', data.access_token);

        return data.access_token;
    }

    // Get stored token
    getToken(): string | null {
        return localStorage.getItem('access_token');
    }

    // Logout and redirect
    logout(): void {
        localStorage.removeItem('access_token');
        window.location.href =
            `${authConfig.authority}${authConfig.endSessionEndpoint}`;
    }
}

export const authService = new AuthService();
Step 5

Create Auth Context Provider

Set up React Context for global authentication state management

// src/auth/AuthProvider.tsx
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import { jwtDecode } from 'jwt-decode';
import { authService } from '../services/AuthService';

interface User {
    userId: string;
    email: string;
    name: string;
    tenantId: number;
    roles: string[];
    permissions: string[];
}

interface AuthContextType {
    user: User | null;
    isAuthenticated: boolean;
    isLoading: boolean;
    login: () => void;
    logout: () => void;
    handleCallback: (code: string) => Promise<void>;
}

const AuthContext = createContext<AuthContextType | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
    const [user, setUser] = useState<User | null>(null);
    const [isLoading, setIsLoading] = useState(true);

    useEffect(() => {
        // Check for existing token on mount
        const token = authService.getToken();
        if (token) {
            try {
                const decoded = jwtDecode<User & { exp: number }>(token);
                if (decoded.exp * 1000 > Date.now()) {
                    setUser(decoded);
                } else {
                    authService.logout();
                }
            } catch {
                authService.logout();
            }
        }
        setIsLoading(false);
    }, []);

    const handleCallback = async (code: string) => {
        const token = await authService.handleCallback(code);
        const decoded = jwtDecode<User>(token);
        setUser(decoded);
    };

    return (
        <AuthContext.Provider value={{
            user,
            isAuthenticated: !!user,
            isLoading,
            login: () => authService.login(),
            logout: () => authService.logout(),
            handleCallback
        }}>
            {children}
        </AuthContext.Provider>
    );
}

export const useAuth = () => {
    const context = useContext(AuthContext);
    if (!context) throw new Error('useAuth must be used within AuthProvider');
    return context;
};
Step 6

Create Callback & Protected Route Components

Handle OAuth callback and protect routes requiring authentication

// src/pages/Callback.tsx - Handle OAuth redirect
import { useEffect } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useAuth } from '../auth/AuthProvider';

export function Callback() {
    const [searchParams] = useSearchParams();
    const { handleCallback } = useAuth();
    const navigate = useNavigate();

    useEffect(() => {
        const code = searchParams.get('code');
        if (code) {
            handleCallback(code)
                .then(() => navigate('/'))
                .catch(() => navigate('/login'));
        }
    }, [searchParams, handleCallback, navigate]);

    return <div>Authenticating...</div>;
}

// src/components/ProtectedRoute.tsx
import { Navigate } from 'react-router-dom';
import { useAuth } from '../auth/AuthProvider';

export function ProtectedRoute({ children }: { children: React.ReactNode }) {
    const { isAuthenticated, isLoading } = useAuth();

    if (isLoading) return <div>Loading...</div>;
    if (!isAuthenticated) return <Navigate to="/login" replace />;

    return <>{children}</>;
}
Step 7

Set Up Routes

Configure React Router with authentication routes

// src/App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { AuthProvider } from './auth/AuthProvider';
import { ProtectedRoute } from './components/ProtectedRoute';
import { Login } from './pages/Login';
import { Callback } from './pages/Callback';
import { Dashboard } from './pages/Dashboard';

function App() {
    return (
        <BrowserRouter>
            <AuthProvider>
                <Routes>
                    <Route path="/login" element={<Login />} />
                    <Route path="/callback" element={<Callback />} />
                    <Route path="/" element={
                        <ProtectedRoute>
                            <Dashboard />
                        </ProtectedRoute>
                    } />
                </Routes>
            </AuthProvider>
        </BrowserRouter>
    );
}

export default App;
Step 8

Create Login & Dashboard Pages

Build the UI components for login and protected dashboard

// src/pages/Login.tsx
import { useAuth } from '../auth/AuthProvider';

export function Login() {
    const { login } = useAuth();

    return (
        <div className="login-container">
            <h1>Welcome to My App</h1>
            <p>Please login to continue</p>
            <button onClick={login}>
                Login with DoorAuth
            </button>
        </div>
    );
}

// src/pages/Dashboard.tsx
import { useAuth } from '../auth/AuthProvider';

export function Dashboard() {
    const { user, logout } = useAuth();

    return (
        <div className="dashboard">
            <h1>Welcome, {user?.name}!</h1>
            <div className="user-info">
                <p><strong>Email:</strong> {user?.email}</p>
                <p><strong>Tenant ID:</strong> {user?.tenantId}</p>
                <p><strong>Roles:</strong> {user?.roles?.join(', ')}</p>
            </div>
            <button onClick={logout}>Logout</button>
        </div>
    );
}
Step 9

Test Your Integration

📚 Working Example: client_todo

See the complete React SPA implementation with SSO Portal integration in the client_todo project. It includes PKCE flow, SSO token handling, and centralized logout.

Step 1

Install Dependencies

Add Passport.js with OpenID Connect strategy

npm install express passport passport-openidconnect express-session
Step 2

Configure Passport Strategy

Set up OpenID Connect strategy with DoorAuth endpoints

const passport = require('passport');
const OpenIDConnectStrategy = require('passport-openidconnect');

passport.use('oidc', new OpenIDConnectStrategy({
    issuer: 'https://localhost:3000',
    authorizationURL: 'https://localhost:3000/api/oauth/authorize',
    tokenURL: 'https://localhost:3000/api/oauth/token',
    userInfoURL: 'https://localhost:3000/api/oauth/userinfo',
    clientID: 'your-client-id',
    clientSecret: 'your-client-secret',
    callbackURL: 'http://localhost:4000/callback',
    scope: ['openid', 'profile', 'email']
}, (issuer, profile, done) => {
    return done(null, profile);
}));

passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));
Step 3

Set Up Express App

Configure Express with session and Passport middleware

const express = require('express');
const session = require('express-session');

const app = express();

app.use(session({
    secret: 'your-session-secret',
    resave: false,
    saveUninitialized: false
}));

app.use(passport.initialize());
app.use(passport.session());

// Login route
app.get('/login', passport.authenticate('oidc'));

// Callback route
app.get('/callback',
    passport.authenticate('oidc', { failureRedirect: '/login' }),
    (req, res) => res.redirect('/')
);

// Logout route
app.get('/logout', (req, res) => {
    req.logout(() => {
        res.redirect('https://localhost:3000/api/oauth/end_session');
    });
});

// Protected route
app.get('/', ensureAuthenticated, (req, res) => {
    res.json({ user: req.user });
});

function ensureAuthenticated(req, res, next) {
    if (req.isAuthenticated()) return next();
    res.redirect('/login');
}

app.listen(4000, () => console.log('App running on http://localhost:4000'));

📚 API Documentation

For direct API integration without Passport.js, visit the Swagger documentation when the server is running.

SSO Portal Integration Pattern

Build an SSO portal that launches applications with seamless authentication

🚀 How SSO Portal Works

The DoorAuthSample demonstrates a powerful SSO Portal pattern where users login once and can launch multiple applications without re-authenticating.

1ī¸âƒŖ
User visits SSO Portal https://localhost:7140
↓
2ī¸âƒŖ
Portal redirects to DoorAuth OIDC authentication flow
↓
3ī¸âƒŖ
User enters credentials bd@gmail.com / 1q2w3E*
↓
4ī¸âƒŖ
Portal fetches user's apps GET /api/users/me/applications
↓
5ī¸âƒŖ
User clicks app card e.g., "Todo App"
↓
6ī¸âƒŖ
App launches with token http://localhost:5175/sso?token=JWT
↓
✅
User auto-logged in! No re-authentication needed

đŸ’ģ Implementation Code

Portal: Launch App with Token (JavaScript)

// In DoorAuthSample portal - launch app with SSO token
function launchApp(appUrl, appName) {
    // Get access token from OIDC session
    const token = document.getElementById('accessToken').value;

    // Construct SSO URL with token
    const cleanAppUrl = appUrl.endsWith('/') ? appUrl.slice(0, -1) : appUrl;
    const ssoUrl = `${cleanAppUrl}/sso?token=${encodeURIComponent(token)}`;

    // Launch the application
    window.location.href = ssoUrl;
}

React App: Handle SSO Token

// src/pages/SSOHandler.tsx - Handle token from portal
import { useEffect } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useAuth } from '../auth/AuthProvider';

export function SSOHandler() {
    const [searchParams] = useSearchParams();
    const { handleSSOToken } = useAuth();
    const navigate = useNavigate();

    useEffect(() => {
        const token = searchParams.get('token');
        if (token) {
            // Validate and store the token
            handleSSOToken(token)
                .then(() => navigate('/'))
                .catch(() => navigate('/login'));
        } else {
            navigate('/login');
        }
    }, [searchParams, handleSSOToken, navigate]);

    return <div>Processing SSO login...</div>;
}

// Add route: <Route path="/sso" element={<SSOHandler />} />

Centralized Logout

// React App - Logout redirects to portal
const logout = () => {
    localStorage.removeItem('access_token');
    // Redirect to portal logout (which logs out from DoorAuth too)
    window.location.href = 'https://localhost:7140/logout';
};

// Portal (ASP.NET) - Cascading logout
app.MapGet("/logout", async (HttpContext context) => {
    await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    await context.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
    // This triggers DoorAuth end_session, logging out everywhere
});

Benefits of SSO Portal Pattern

🔐

Single Login

Users authenticate once and access all applications

🚀

Instant App Launch

No login prompts when launching apps from portal

🔄

Centralized Logout

Single logout clears sessions across all applications

📱

App Discovery

Portal shows only apps user has access to

Comprehensive API

40+ RESTful endpoints with full Swagger documentation

🔐 Authentication (3)

POST /api/auth/register
POST /api/auth/login
POST /api/auth/logout

🔑 OAuth/OIDC (5)

GET /api/oauth/authorize
POST /api/oauth/token
GET /api/oauth/userinfo
POST /api/oauth/revoke
GET /api/oauth/end_session

đŸĸ Tenant Management (5)

GET /api/tenants
GET /api/tenants/:id
POST /api/tenants
PUT /api/tenants/:id
DELETE /api/tenants/:id

📱 Application Management (6)

GET /api/applications
POST /api/applications
PUT /api/applications/:id
DELETE /api/applications/:id
POST /api/applications/:id/regenerate-secret

đŸŽ¯ Role & Permissions (7)

GET /api/roles
POST /api/roles
PUT /api/roles/:id
DELETE /api/roles/:id
POST /api/roles/:id/permissions

🔒 Security Features

POST /api/2fa/generate
POST /api/2fa/verify
POST /api/password/forgot-password
POST /api/password/reset-password

Interactive API Documentation

Explore and test all endpoints with Swagger UI

Open Swagger UI

Complete Documentation

Everything you need to integrate and deploy DoorAuth