A self-hosted employee identity verification portal powered by Microsoft Entra Verified ID
3.4K
VerifID is a contact verification portal based on Microsoft Entra Verified ID.
It allows HR teams to provide verifiable credentials to employees. It also provides support teams with a way to verify their identity without a password, using a QR code and Microsoft Authenticator.
The goal is to limit identity theft and fraud.
Microsoft Verified ID provides:
This development offers a user-friendly interface focused on verifying the identity of the person you are interacting with.
Verified IDā For more information, you can visit the Microsoft website.
The development carried out is based on the examples provided by Microsoft in the Azure-Samples repositoryā
VerifiedEmployee verifiable credential directly into the employee's Microsoft Authenticator wallethelpdesk, hr)| Layer | Technology |
|---|---|
| Frontend | Angular 21 ā Signals, Signal Forms, Zoneless, Standalone Components |
| Styling | Bootstrap 5.3 + Bootstrap Icons 1.13 |
| Backend | FastAPI 0.135 + Python 3.14 (fully async) |
| Validation | Pydantic v2 |
| Identity | Microsoft Entra Verified ID (Request Service API) |
| Auth | MSAL (frontend) + python-jose JWT validation (backend) |
| Container | Single-stage Docker image ā python:3.14-alpine |
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Docker Container ā
ā ā
ā āāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāāāāā ā
ā ā Angular ā ā FastAPI ā ā
ā ā (static āāāāā /api/v1/... ā ā
ā ā files) ā ā ā ā
ā āāāāāāāāāāāāāāāā āāāāāāāāāā¬āāāāāāāāāāāāā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāā
ā
āāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāā
ā Microsoft Entra Verified ID ā
ā (verifiedid.did.msidentity.com)ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
VerifiedEmployee credential typedocker run -d \
--name verifid \
-p 8080:8080 \
-e AZURE_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
-e AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
-e AZURE_CLIENT_SECRET=your-client-secret \
-e VERIFIED_ID_DID=did:web:yourdomain.com \
-e VERIFIED_ID_CONTRACT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
-e APP_BASE_URL=https://yourdomain.com \
-e API_KEY=your-api-key \
-e AUTH_CLIENT_ID=xxxxxxx-xxx-xxxx-xxxx-xxxxxxxxxxxx \
ghcr.io/cyr-ius/verifid:latest
Open http://localhost:8000ā to access the portal.
services:
verifid:
image: ghcr.io/cyr-ius/verifid:latest
container_name: verifid
restart: unless-stopped
ports:
- "8080:8080"
environment:
- AZURE_TENANT_ID=${AZURE_TENANT_ID}
- AZURE_CLIENT_ID=${AZURE_CLIENT_ID}
- AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET}
- VERIFIED_ID_DID=${VERIFIED_ID_DID}
- VERIFIED_ID_CONTRACT_ID=${VERIFIED_ID_CONTRACT_ID}
- APP_BASE_URL=${APP_BASE_URL}
- API_KEY=${API_KEY}
- AUTH_CLIENT_ID=${AUTH_CLIENT_ID}
Then create a .env file from the provided example:
cp backend/.env.example .env
# Edit .env with your actual values
docker compose up -d
/verify and clicks Start verification/assist dashboard using the assistance code/issue (requires hr role)VerifiedEmployee credential is stored in the employee's wallet, ready to be presented to the helpdesk| Variable | Description |
|---|---|
AZURE_TENANT_ID | Azure Active Directory tenant ID |
AZURE_CLIENT_ID | Client ID of the Backend API app registration (Verif ID API interface) |
AZURE_CLIENT_SECRET | Client secret for the backend app registration |
VERIFIED_ID_DID | Your Verified ID authority DID (e.g. did:web:yourdomain.com) |
VERIFIED_ID_CONTRACT_ID | Credential contract ID used to build the Verified ID issuance manifest URL |
APP_BASE_URL | Public HTTPS base URL of the application ā used by Microsoft to POST callbacks |
API_KEY | Shared secret that Microsoft must include in callback headers |
AUTH_CLIENT_ID | Client ID of the Frontend SPA app registration (Verif ID interface) |
| Variable | Description | Default |
|---|---|---|
AUTH_ENABLED | Enable JWT authentication on protected endpoints | True |
AUTH_AUDIENCE | Comma-separated accepted JWT audiences | AZURE_CLIENT_ID |
AUTH_SCOPE | Delegated scope accepted for helpdesk access | access_as_user |
AUTH_JWKS_CACHE_TTL_SECONDS | Time to cache JWKS keys (reduce latency) | 3600 |
AUTH_ROLE_HELPDESK | Comma-separated list of valid roles | helpdesk |
AUTH_ROLE_HR | Comma-separated list of valid roles | hr |
| Variable | Description | Default |
|---|---|---|
SWAGGER_ENABLE | Expose /api/docs and /api/openapi.json | False |
LOG_LEVEL | Log level (DEBUG, INFO, WARNING, ERROR) | INFO |
LOGO_URL | URL of the logo displayed in the navigation bar | placeholder |
Two App Registrations are required: one for the Backend API (used by the FastAPI service to call the Verified ID API and validate inbound JWTs), and one for the Frontend SPA (used by the Angular application to authenticate users via MSAL).
This registration allows the backend to:
helpdesk, hr) and a delegated scope (access_as_user)VerifID ā Backend API (or any name you prefer)AZURE_CLIENT_IDAZURE_TENANT_IDverifid-backend) and choose an expiryAZURE_CLIENT_SECRETā ļø The secret value is only shown once. Store it securely in a vault or as a Docker secret.
api://<client-id>) or use a custom URIaccess_as_userAccess VerifID as a userAllows the application to access the VerifID helpdesk API on behalf of the signed-in userApplication roles are used for server-to-server authorization (helpdesk, hr).
Role 1 ā Helpdesk
| Field | Value |
|---|---|
| Display name | Helpdesk |
| Allowed member types | Users/Groups |
| Value | helpdesk |
| Description | Access to helpdesk identity verification features |
| State | Enabled |
Role 2 ā HR
| Field | Value |
|---|---|
| Display name | HR |
| Allowed member types | Users/Groups |
| Value | hr |
| Description | Access to HR credential issuance features |
| State | Enabled |
The backend needs to call the Verified ID Request Service using its own identity (client credentials flow).
3db474b9-6a0c-4840-96ac-1fceb342124f)VerifiableCredential.Create.All.env variable | Where to find it |
|---|---|
AZURE_TENANT_ID | Overview ā Directory (tenant) ID |
AZURE_CLIENT_ID | Overview ā Application (client) ID |
AZURE_CLIENT_SECRET | Certificates & secrets ā the value you copied |
AUTH_AUDIENCE | api://<client-id> or the Application ID URI |
This registration allows the Angular application to sign users in via MSAL and obtain access tokens that are forwarded to the FastAPI backend.
VerifID ā Frontend SPAhttps://yourdomain.com (add http://localhost:4200 during development)AUTH_CLIENT_IDhttp://localhost:4200 for local dev)The frontend needs permission to call the backend API using the scope created in App 1.
access_as_userRoles are assigned via Enterprise Applications, not App Registrations.
Helpdesk or HR)Role assignments are reflected in the
rolesclaim of the access token. Users without an assigned role will receive a 403 on protected endpoints.
.env variable | Where to find it |
|---|---|
AUTH_CLIENT_ID | Overview ā Application (client) ID of the SPA registration |
| Purpose | App Registration | Key values |
|---|---|---|
| Backend token acquisition & JWT validation | VerifID ā Backend API | AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AUTH_AUDIENCE |
| Frontend user sign-in (MSAL) | VerifID ā Frontend SPA | AUTH_CLIENT_ID |
| Role / Scope | Grants access to |
|---|---|
helpdesk (app role) | /api/v1/verified-id/assist/{code} |
hr (app role) | /api/v1/verified-id/issue |
| (no auth) | /verify page and /api/v1/verified-id/verify |
Authentication can be disabled entirely by setting AUTH_ENABLED=False ā useful during initial setup and testing.
A ready-to-use VS Code Dev Container is included (.devcontainer/). Open the repository in VS Code and select Reopen in Container to get a fully configured environment with Python, Node, Angular CLI, and all dependencies installed automatically.
cd backend
cp .env.example .env # fill in your Azure values
uv sync
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
API docs are available at http://localhost:8000/api/docs when SWAGGER_ENABLE=True.
cd frontend
npm install
ng serve --host 0.0.0.0 # proxies /api/* to http://localhost:8000
The Angular dev server starts at http://localhost:4200.
docker compose up --build
A GitHub Actions workflow can be added to build and publish the Docker image automatically on every push to main and on version tags. The recommended tag strategy is latest, X.Y.Z, and sha-<commit>.
GET /api/health returns:
{ "status": "healthy", "service": "VerifID API" }
MIT ā see LICENSEā for details.
Author: @cyr-iusā
Content type
Image
Digest
sha256:c934647d8ā¦
Size
78.7 MB
Last updated
1 day ago
docker pull cyrius44/verifid