Skip to content

daniel-fraubaum/laps-self-service-portal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

7 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ” LAPS Self-Service Portal

A self-service portal for Windows LAPS β€” lets users securely retrieve the local administrator password for their own device, without a helpdesk ticket.

Built on Azure Static Web Apps + Azure Functions, secured with Entra ID. Zero stored secrets for API access.


✨ Features

  • πŸ–₯️ Only-my-device rule β€” users can only see and retrieve passwords for devices registered to them (enforced server-side)
  • πŸ“ Mandatory justification β€” configurable minimum length (default: 10 characters)
  • ⏱️ 60-second auto-hide β€” password disappears with a countdown bar; copy available during the window
  • πŸ“‹ Audit log β€” every access attempt written to Azure Table Storage with user, device, justification, and IP
  • πŸ”‘ Managed Identity β€” no stored secrets for Graph API access
  • πŸ›‘οΈ Easy Auth β€” Entra ID JWT validated by Azure before your code runs; invalid or missing tokens rejected with 401 automatically
  • 🎨 CSS theming β€” full white-labeling via CSS custom properties
  • 🌐 Custom domain β€” supported via Azure Static Web Apps (Standard tier)

🧩 How it works

  1. πŸ‘€ User signs in with their Entra ID account
  2. πŸ–₯️ The portal shows only their own registered devices (enforced server-side)
  3. πŸ“ User selects a device and enters a justification
  4. πŸ”“ LAPS password is shown for 60 seconds, then hidden automatically
  5. πŸ“‹ Every access attempt is logged to Azure Table Storage

πŸ“Έ Screenshots

Sign-in Justification
Sign-in screen Request justification dialog
Password (60 s countdown) Auto-expired
Password visible with countdown Time expired dialog
Audit log (Azure Table Storage)
Audit log in Azure Portal

βœ… Prerequisites

πŸ› οΈ Tools

Tool Min. Version Install
Azure CLI 2.50 aka.ms/installazurecli
Node.js 24 LTS nodejs.org β€” includes npm
Static Web Apps CLI latest npm install -g @azure/static-web-apps-cli

πŸ” Azure permissions

Scope Role
Azure subscription Contributor
Entra ID tenant Application Administrator (or Global Administrator)
Entra ID tenant Privileged Role Administrator

πŸ’‘ A Global Administrator covers all of the above by default.


πŸš€ Deploy

🍎 macOS / Linux / WSL

1. Install prerequisites (once):

# macOS (Homebrew)
brew install azure-cli node
npm install -g @azure/static-web-apps-cli

2. Clone and deploy:

git clone https://github.com/daniel-fraubaum/laps-self-service-portal.git
cd laps-self-service-portal
chmod +x infra/deploy.sh
./infra/deploy.sh --project laps-prod

πŸͺŸ Windows (PowerShell)

1. Install prerequisites (once):

  • πŸ”΅ Azure CLI β€” download and run the MSI installer
  • 🟒 Node.js 24 LTS β€” download and run the installer (includes npm)
  • πŸ“¦ Static Web Apps CLI:
    npm install -g @azure/static-web-apps-cli

2. Clone and deploy:

git clone https://github.com/daniel-fraubaum/laps-self-service-portal.git
cd laps-self-service-portal
.\infra\deploy.ps1 -Project laps-prod

The script asks for confirmation, then handles everything end-to-end (~10 minutes):

Step Action
1️⃣ Creates (or reuses) the Entra ID App Registration, sets the identifier URI
2️⃣ Deploys all Azure infrastructure via Bicep (single pass)
3️⃣ Assigns Device.Read.All, DeviceLocalCredential.Read.All, Directory.Read.All to the Managed Identity
4️⃣ Deploys the backend (Azure Functions)
5️⃣ Generates frontend/authConfig.js from deployment outputs
6️⃣ Deploys the frontend (Azure Static Web App)
7️⃣ Registers the Static Web App URL as a redirect URI on the App Registration
8️⃣ Grants admin consent for User.Read

⏳ Allow a few minutes after deployment before the portal is fully functional. Graph API role assignments (Managed Identity permissions) can take 2–5 minutes to propagate in Entra ID. If you see 403 / permission errors right after deployment, simply wait and refresh.


☁️ What gets deployed

Resource Details
πŸ“¦ Resource Group rg-<projectName>
πŸ“Š Log Analytics + Application Insights Telemetry and custom events
πŸ’Ύ Storage Account Runtime storage + LapsAuditLog audit table
βš™οΈ App Service Plan Linux B1
🌐 Azure Static Web App Standard tier (SPA frontend)
⚑ Azure Function App Node.js 24, system-assigned Managed Identity
πŸ” Entra ID App Registration MSAL user login + Easy Auth JWT validation

πŸ’° Estimated cost

All prices are approximate, based on West Europe, low/idle usage. Billed in USD by Azure.

Resource Tier ~$/month
βš™οΈ App Service Plan Linux B1 ~$13
🌐 Azure Static Web App Standard ~$9
πŸ’Ύ Storage Account LRS, minimal usage ~$1
πŸ“Š Log Analytics + App Insights Pay-per-use, minimal ingestion ~$1
⚑ Function App Runs on B1 plan (no extra charge) β€”
πŸ” Entra ID App Registration Free tier β€”
Total ~$24/month

πŸ”’ Restricting access

⚠️ Security requirement β€” do this before going live. By default, any user in your Entra ID tenant can log in to the portal. Enable assignment enforcement immediately after deployment.

  1. 🏒 Entra ID β†’ Enterprise Applications β†’ search for your app (e.g. laps-prod-laps-portal)
  2. βš™οΈ Properties β†’ set "Assignment required?" to Yes β†’ Save
  3. πŸ‘₯ Users and groups β†’ Add assignment β†’ select your security group (e.g. SG-LAPS-Self-Service)

Unassigned users receive AADSTS50105 from Entra ID at sign-in β€” the portal and backend never see the request.

πŸ’‘ Always assign a security group rather than individual users β€” group membership can then be managed independently in Entra ID.

See docs/CONFIGURATION.md for CLI commands and details.

πŸ›‘οΈ Enforcing MFA

⚠️ MFA cannot be enforced from the application code. MSAL can request a step-up challenge, but this is bypassable β€” a user with a valid token from another app could call the backend API directly. The only secure enforcement is via Entra ID Conditional Access.

Create a Conditional Access Policy targeting your app:

  1. Entra ID β†’ Security β†’ Conditional Access β†’ + New policy
  2. Users: your LAPS security group (e.g. SG-LAPS-Self-Service)
  3. Target resources: select your App Registration (e.g. laps-prod-laps-portal)
  4. Grant: βœ”οΈ Require multi-factor authentication
  5. Enable policy: On β†’ Create

Entra ID then enforces MFA at token issuance β€” before the portal or backend sees anything.


πŸ”„ Re-deploy / updates

# 🍎 macOS / Linux
./infra/deploy.sh --project laps-prod
# πŸͺŸ Windows PowerShell
.\infra\deploy.ps1 -Project laps-prod

🧩 Partial deploys

# 🍎 macOS / Linux β€” code only (skip Bicep)
./infra/deploy.sh --project laps-prod --skip-infra

# 🍎 macOS / Linux β€” infrastructure only (skip backend + frontend)
./infra/deploy.sh --project laps-prod --skip-backend --skip-frontend
# πŸͺŸ Windows PowerShell β€” code only (skip Bicep)
.\infra\deploy.ps1 -Project laps-prod -SkipInfra

# πŸͺŸ Windows PowerShell β€” infrastructure only
.\infra\deploy.ps1 -Project laps-prod -SkipBackend -SkipFrontend

🌐 Custom domain

# 🍎 macOS / Linux
./infra/deploy.sh \
  --project  laps-prod \
  --location westeurope \
  --domain   laps.company.com
# πŸͺŸ Windows PowerShell
.\infra\deploy.ps1 `
  -Project      laps-prod `
  -Location     westeurope `
  -CustomDomain laps.company.com

⚠️ After enabling a custom domain, add the new URL as a redirect URI on the App Registration. Open Entra ID β†’ App registrations β†’ your-app β†’ Authentication β†’ Redirect URIs and add https://laps.company.com. Logins will fail with AADSTS50011 until this is done. See docs/deployment.md for CLI commands.


πŸ—οΈ Architecture

[User Browser]
     β”‚  MSAL Login (Authorization Code + PKCE)
     β–Ό
[Azure Static Web App]       ← Single-file SPA (index.html + MSAL.js)
     β”‚  Bearer Token (Entra ID JWT)
     β–Ό
[Azure Function App]         ← Node.js 24, Easy Auth validates JWT before code runs
     β”‚  App-only token (Managed Identity, no stored secrets)
     β–Ό
[Microsoft Graph]
     β”œβ”€β”€ /users/{id}/registeredDevices      ← ownership check (only-my-device rule)
     └── /deviceLocalCredentials/{id}       ← LAPS password retrieval

[Azure Table Storage]        ← LapsAuditLog (every access attempt, success or failure)
[Application Insights]       ← Telemetry and custom events

πŸ“ Project structure

β”œβ”€β”€ infra/
β”‚   β”œβ”€β”€ deploy.sh                      ← Full deployment (Bash β€” macOS/Linux/WSL)
β”‚   β”œβ”€β”€ deploy.ps1                     ← Full deployment (PowerShell β€” Windows)
β”‚   β”œβ”€β”€ main.bicep                     ← Bicep template (subscription scope)
β”‚   β”œβ”€β”€ bicepconfig.json               ← Bicep linting rules
β”‚   β”œβ”€β”€ main.parameters.example.json   ← Parameters template
β”‚   └── modules/
β”‚       β”œβ”€β”€ monitoring.bicep           ← Log Analytics + Application Insights
β”‚       β”œβ”€β”€ storage.bicep              ← Storage Account + audit table
β”‚       β”œβ”€β”€ appServicePlan.bicep       ← Linux App Service Plan (B1)
β”‚       β”œβ”€β”€ staticwebapp.bicep         ← Frontend Static Web App
β”‚       └── functionapp.bicep          ← Backend Function App + Easy Auth
β”œβ”€β”€ frontend/
β”‚   β”œβ”€β”€ index.html                     ← Single-page SPA (CSS + JS fully embedded)
β”‚   β”œβ”€β”€ authConfig.example.js          ← Template – copy to authConfig.js and fill in
β”‚   β”œβ”€β”€ staticwebapp.config.json       ← SWA routing + security headers
β”‚   └── css/
β”‚       └── theme.css                  ← Branding overrides (colors, fonts, logo)
β”œβ”€β”€ backend/
β”‚   β”œβ”€β”€ host.json
β”‚   β”œβ”€β”€ package.json
β”‚   β”œβ”€β”€ eslint.config.js               ← ESLint 9 flat config
β”‚   └── src/
β”‚       β”œβ”€β”€ index.js                   ← Entry point (boots telemetry + registers functions)
β”‚       β”œβ”€β”€ functions/
β”‚       β”‚   β”œβ”€β”€ myDevices.js           ← GET  /api/my-devices
β”‚       β”‚   └── lapsPassword.js        ← POST /api/laps-password
β”‚       └── lib/
β”‚           β”œβ”€β”€ auth.js                ← Easy Auth + JWKS JWT verification
β”‚           β”œβ”€β”€ graph.js               ← Microsoft Graph (DefaultAzureCredential)
β”‚           β”œβ”€β”€ telemetry.js           ← Application Insights custom events
β”‚           └── audit.js               ← Azure Table Storage audit log
└── docs/
    β”œβ”€β”€ architecture.md                ← Architecture overview + auth flow
    β”œβ”€β”€ deployment.md                  ← Detailed deployment guide
    └── CONFIGURATION.md               ← All config values explained

Note on App Registration: Created and configured by the deploy scripts via az ad app CLI β€” not via a Bicep module.


πŸ“š Documentation


βš–οΈ License

MIT β€” see LICENSE

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors