Microsoft Integration Setup
All Microsoft integrations in BonardaHR are optional — the application works fully in dev mode without any Azure credentials. This guide covers how to configure each integration for staging and production environments.
Integration Overview​
| Integration | Purpose | Default Dev Behaviour |
|---|---|---|
| Azure AD SSO | "Sign in with Microsoft" button | Dev login dropdown (no SSO button shown) |
| SharePoint | Document browsing, upload, and preview | Mock service returns realistic fake data |
| Outlook Calendar Sync | Sync approved time-off to employee calendars | Events logged to console only |
Dev Workflow Summary​
| Scenario | Config | What Works |
|---|---|---|
| Quick local dev | Default (no env vars) | Mock SharePoint + dev login (no SSO) |
| SSO testing only | Set AZURE_AD_* vars (free Azure account) | Real Microsoft login + mock SharePoint |
| Full integration | Set AZURE_AD_* + MS_* + SP_MOCK_ENABLED=false | Real SSO + real SharePoint + calendar sync |
Azure AD SSO Setup​
Prerequisites​
- A Microsoft account (personal or work)
- A free Azure account: https://azure.microsoft.com/free/
A regular free Azure account includes Azure AD (Entra ID) at no cost — no M365 Developer Program required.
Step 1 — Register an App in Azure​
- Sign in to Azure Portal
- Navigate to Azure Active Directory → App registrations → New registration
- Configure the registration:
- Name:
BonardaHR(orBonardaHR Dev) - Supported account types: Accounts in this organizational directory only
- Redirect URI: Select Web and enter:
http://localhost:8081/login/oauth2/code/azure
- For production, also add:
https://your-domain.com/login/oauth2/code/azure
- Name:
- Click Register
Step 2 — Add API Permissions​
- Go to API permissions → Add a permission → Microsoft Graph → Delegated permissions
- Add the following permissions:
openidprofileemailUser.Read
- Click Grant admin consent for [Your Organization]
Step 3 — Create a Client Secret​
- Go to Certificates & secrets → New client secret
- Set a description and expiry (24 months recommended)
- Copy the secret value immediately — it's only shown once
Step 4 — Configure Environment Variables​
Add the following to your backend/.env file:
AZURE_AD_ENABLED=true
AZURE_AD_TENANT_ID=your-directory-tenant-id
AZURE_AD_CLIENT_ID=your-application-client-id
AZURE_AD_CLIENT_SECRET=your-client-secret-value
AZURE_AD_FRONTEND_CALLBACK_URL=http://localhost:5173/auth/callback
Find your Tenant ID and Client ID in Azure Portal → App registrations → your app → Overview.
Step 5 — Create Test Users​
In Azure Active Directory → Users → New user, create users with emails matching your seeded employees (or create new employees in BonardaHR with matching emails).
Testing SSO​
- Start the backend:
./mvnw spring-boot:run - Start the frontend:
npm run dev - Navigate to
http://localhost:5173/login - You should see a "Sign in with Microsoft" button
- Click it → Authenticate with a test user → You'll be redirected back to the app
Check SSO status via API:
curl http://localhost:8081/api/v1/auth/sso-config
Expected when enabled:
{
"ssoEnabled": true,
"ssoUrl": "http://localhost:8081/oauth2/authorization/azure"
}
SharePoint Integration Setup​
SharePoint integration uses application-level permissions (client credentials flow), so it does not require user delegation.
Prerequisites​
Your Azure tenant must have SharePoint Online (requires an M365 license). If you only have a free Azure account, use the mock service.
Step 1 — Register an App for SharePoint​
You can create a second app registration or add permissions to the existing SSO app.
- Go to App registrations → New registration (or use your existing SSO app)
- Go to API permissions → Add a permission → Microsoft Graph → Application permissions
- Add:
Sites.ReadWrite.AllFiles.ReadWrite.All
- Click Grant admin consent for [Your Organization]
- Create a client secret (Certificates & secrets → New client secret)
Step 2 — Find Your SharePoint Site and Drive IDs​
Use Microsoft Graph Explorer to find your site and drive IDs:
GET https://graph.microsoft.com/v1.0/sites?search=your-site-name
GET https://graph.microsoft.com/v1.0/sites/{site-id}/drives
Step 3 — Configure Environment Variables​
# Microsoft Graph (SharePoint + Calendar)
MS_TENANT_ID=your-tenant-id
MS_CLIENT_ID=your-client-id
MS_CLIENT_SECRET=your-client-secret-value
SP_MOCK_ENABLED=false
# Optional: Quick Access library shortcuts
SP_POLICIES_SITE_ID=contoso.sharepoint.com,abc-123,def-456
SP_POLICIES_DRIVE_ID=b!abc123def456
SP_PERSONNEL_SITE_ID=contoso.sharepoint.com,ghi-789,jkl-012
SP_PERSONNEL_DRIVE_ID=b!ghi789jkl012
Testing SharePoint​
- Start the app with the env vars above
- Navigate to Documents → Add Document → Browse SharePoint
- You should see your organisation's SharePoint sites
Check SharePoint status via API:
curl -H "Authorization: Bearer <your-jwt>" http://localhost:8081/api/v1/sharepoint/status
Expected when configured:
{
"configured": true,
"libraries": [
{ "key": "policies", "name": "Company Policies", "configured": true },
{ "key": "personnel", "name": "Personnel Documents", "configured": true }
]
}
Outlook Calendar Sync Setup​
Calendar sync uses the same MS_* credentials as SharePoint — no additional app registration is needed. You only need to add the Calendars.ReadWrite application permission.
Step 1 — Add Calendar Permission​
In Azure Portal → your Microsoft Graph app → API permissions → Application permissions, add:
Calendars.ReadWrite
Then click Grant admin consent.
Step 2 — Link Employees to Azure AD​
For calendar sync to work, an employee's microsoftUserId must be set in BonardaHR. This is populated automatically when the employee first signs in via SSO. For employees who don't use SSO, set it manually in the employee detail page.
Testing Calendar Sync​
- Ensure
MS_*env vars are configured andCalendars.ReadWriteis granted - Create or identify a test employee with a matching
microsoftUserId - Submit a time-off request for that employee
- Log in as a manager and Approve the request
- Check the employee's Outlook calendar — a new event should appear
Check sync status on a request:
curl -H "Authorization: Bearer <your-jwt>" http://localhost:8081/api/v1/time-off-requests/me
Look for "calendarSynced": true on approved requests.
Cancelling an approved request will delete the calendar event.
Complete .env Reference​
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=bonarda_hr
DB_USERNAME=postgres
DB_PASSWORD=postgres
# JWT (generate a secure random string for production — minimum 32 chars)
JWT_SECRET=your-256-bit-secret-key-here-minimum-32-chars
# Azure AD SSO
AZURE_AD_ENABLED=true
AZURE_AD_TENANT_ID=12345678-1234-1234-1234-123456789012
AZURE_AD_CLIENT_ID=abcdef12-3456-7890-abcd-ef1234567890
AZURE_AD_CLIENT_SECRET=your-client-secret-value
AZURE_AD_FRONTEND_CALLBACK_URL=http://localhost:5173/auth/callback
# SharePoint / Microsoft Graph
MS_TENANT_ID=12345678-1234-1234-1234-123456789012
MS_CLIENT_ID=fedcba09-8765-4321-fedc-ba0987654321
MS_CLIENT_SECRET=another-client-secret-value
SP_MOCK_ENABLED=false
# Optional: Quick Access library shortcuts
SP_POLICIES_SITE_ID=contoso.sharepoint.com,abc-123,def-456
SP_POLICIES_DRIVE_ID=b!abc123def456
SP_PERSONNEL_SITE_ID=contoso.sharepoint.com,ghi-789,jkl-012
SP_PERSONNEL_DRIVE_ID=b!ghi789jkl012
Environment Variable Reference​
Azure AD SSO
| Variable | Required | Default | Description |
|---|---|---|---|
AZURE_AD_ENABLED | Yes | false | Set to true to enable SSO |
AZURE_AD_TENANT_ID | If enabled | — | Azure AD directory (tenant) ID |
AZURE_AD_CLIENT_ID | If enabled | — | Application (client) ID |
AZURE_AD_CLIENT_SECRET | If enabled | — | Client secret value |
AZURE_AD_FRONTEND_CALLBACK_URL | No | http://localhost:5173/auth/callback | Redirect after successful auth |
SharePoint / Microsoft Graph
| Variable | Required | Default | Description |
|---|---|---|---|
MS_TENANT_ID | If SP_MOCK_ENABLED=false | — | Azure AD tenant ID |
MS_CLIENT_ID | If SP_MOCK_ENABLED=false | — | Application (client) ID |
MS_CLIENT_SECRET | If SP_MOCK_ENABLED=false | — | Client secret value |
SP_MOCK_ENABLED | No | true | Set to false to use real SharePoint |
SP_POLICIES_SITE_ID | No | mock-site-hr | Site ID for Policies library |
SP_POLICIES_DRIVE_ID | No | mock-drive-hr-docs | Drive ID for Policies library |
SP_PERSONNEL_SITE_ID | No | mock-site-intranet | Site ID for Personnel library |
SP_PERSONNEL_DRIVE_ID | No | mock-drive-intranet-docs | Drive ID for Personnel library |
Troubleshooting​
SSO: "AADSTS50011: Reply URL does not match"​
Cause: The redirect URI in Azure doesn't match the callback URL.
Fix: Azure Portal → App registrations → your app → Authentication → add the exact redirect URI:
http://localhost:8081/login/oauth2/code/azure
SSO: "User not found" after authentication​
Cause: The authenticated user's email doesn't match any employee in the database.
Fix: Create an employee in BonardaHR with the same email as the Azure AD user, or create an Azure AD user with the email of a seeded employee.
SharePoint: "Access denied" or 403 errors​
Cause: Missing admin consent for application permissions.
Fix: Azure Portal → your app → API permissions → Grant admin consent for [Your Organization]. Ensure all permissions show a green checkmark.
SharePoint: "Configured: false" even with credentials​
Cause: Invalid credentials or SP_MOCK_ENABLED is still true.
Fix: Verify SP_MOCK_ENABLED=false, double-check tenant/client IDs and secret, and check backend logs for specific errors.
Calendar: Events not appearing in Outlook​
Cause: Missing Calendars.ReadWrite permission or employee has no microsoftUserId.
Fix:
- Verify
Calendars.ReadWritepermission is granted with admin consent - Ensure the employee has a
microsoftUserIdset (auto-populated on first SSO login) - Verify the
microsoftUserIdmatches a real user in your Azure AD tenant
Calendar: "calendarSynced": false on approved requests​
Cause: Calendar sync failed (approval still succeeded).
Fix: Check backend logs for the specific error. Common causes:
- Employee has no
microsoftUserId(expected — will be silently skipped) - Invalid
microsoftUserId Calendars.ReadWritepermission not granted- Microsoft Graph API quota exceeded (rare)
Calendar sync is best-effort — failures do not roll back time-off approvals. See ADR-023 for the rationale.
General: Environment variables not loading​
Fix:
- Ensure
.envfile is in thebackend/directory (not project root) - Restart the backend after modifying
.env - Check for typos in variable names (case-sensitive)
Security Best Practices​
- Never commit secrets — ensure
.envis in.gitignore - Use separate app registrations for dev and production environments
- Rotate secrets regularly — Azure client secrets can expire
- Use managed identities when hosting on Azure (eliminates the need for secrets entirely)
- Limit permissions — only grant the permissions listed above
- Monitor usage — review Azure AD sign-in logs regularly for anomalies