Identity Provider Configuration¶
Overview¶
Cesivi Server supports multiple authentication methods to fit different use cases, from simple development testing to production-grade enterprise deployments.
Available Providers: - AcceptAll - Zero-configuration default for development - OAuth2/OIDC - Modern JWT Bearer tokens for production - NTLM - Windows/Enterprise authentication - TrustedCaller - Identity delegation from trusted applications (WebUI) - ConfigUsers - Static config-defined Basic/Forms users for tests, demos, and E2E fixtures
All providers use a priority-based selection system - the server tries providers in order until one successfully authenticates the request.
Quick Comparison¶
| Provider | Best For | Setup Complexity | Use Cases |
|---|---|---|---|
| AcceptAll | Development, Testing | None | Local development, unit tests, demos |
| OAuth2/OIDC | Production, SPA, Mobile | Medium | Cloud apps, microservices, modern web apps |
| NTLM | Enterprise, Legacy apps | Low-Medium | Desktop apps, intranet, SharePoint migrations |
| TrustedCaller | WebUI, Service-to-Service | Medium | Identity delegation from trusted apps |
| ConfigUsers | Tests, demos, E2E fixtures | Low | Named test users with predictable groups/permissions — never production |
AcceptAll Provider (Default)¶
The simplest authentication mode - accepts all credentials without validation.
When to Use¶
- Local development and testing
- Automated tests (unit, integration)
- Proof-of-concept demos
- Internal sandboxes
Configuration¶
Enabled by default - no configuration needed!
{
"Cesivi": {
"Identity": {
"Providers": {
"AcceptAll": {
"Enabled": true,
"Priority": 1000,
"DefaultUsername": "SHAREPOINT\\administrator"
}
}
}
}
}
Example Usage¶
// CSOM - any credentials work
var context = new ClientContext("http://localhost:5000");
context.Credentials = new NetworkCredential("user", "password");
# PnP PowerShell - any credentials work
Connect-PnPOnline -Url "http://localhost:5000" -Credentials (Get-Credential)
OAuth2/OIDC Provider¶
Modern authentication using JWT Bearer tokens from identity providers like Azure AD, Okta, Auth0, Keycloak.
When to Use¶
- Production deployments
- Single Page Applications (SPA)
- Mobile apps
- Microservices architectures
- Multi-tenant scenarios
Quick Start¶
- Enable OAuth2 provider in
appsettings.json - Configure your identity provider (Azure AD, Okta, etc.)
- Get a token from your IdP
- Use Bearer token in requests
{
"Cesivi": {
"Identity": {
"Providers": {
"OAuth2": {
"Enabled": true,
"Priority": 50,
"Authority": "https://login.microsoftonline.com/{tenant-id}/v2.0",
"Audience": "api://cesivi"
}
}
}
}
}
Example Usage¶
# Get token from identity provider
TOKEN=$(curl ... get token ...)
# Use with Cesivi
curl http://localhost:5000/_api/web \
-H "Authorization: Bearer $TOKEN"
See: OAuth2 Setup Guide for detailed configuration
NTLM Provider¶
Windows/Enterprise authentication with Type 1/2/3 challenge-response protocol.
When to Use¶
- Enterprise environments with Active Directory
- Desktop applications (SharePoint Designer, InfoPath)
- Intranet scenarios
- SharePoint migration projects
- Compatibility with legacy tools
Quick Start¶
Three backends available: 1. Configuration-based - Define users in appsettings.json (default) 2. Active Directory - Authenticate against real AD via LDAP (✅ fully implemented) 3. Pure LDAP - OpenLDAP, 389 DS, FreeIPA, etc. (✅ fully implemented)
Configuration-Based Users¶
{
"Cesivi": {
"Identity": {
"Providers": {
"NTLM": {
"Enabled": true,
"Priority": 100,
"Backend": "Configuration",
"Configuration": {
"Users": [
{
"Username": "testuser",
"Domain": "CONTOSO",
"Password": "Test@1234",
"Email": "testuser@contoso.com",
"DisplayName": "Test User"
}
]
}
}
}
}
}
}
Example Usage¶
// CSOM with NTLM
var context = new ClientContext("http://localhost:5000");
context.Credentials = new NetworkCredential("testuser", "Test@1234", "CONTOSO");
See: NTLM Setup Guide for detailed configuration
ConfigUsers Provider¶
⚠️ NEVER enable in production. Passwords are stored and compared in plaintext (a constant-time compare only guards against timing side channels — it does not add hashing, salting, rate-limiting, or lockout). This provider exists for tests, demos, and local dev fixtures where you need named, predictable users without standing up a real IDP.
A static, config-defined list of Basic + Forms users and (optionally nested) groups. Disabled
by default (Enabled: false) — the server does not register the provider at all unless
explicitly turned on.
When to Use¶
- Playwright/E2E test fixtures that need stable usernames, groups, and permission levels
- Local dev or demo environments where NTLM/OAuth2/SAML setup is overkill
- Reproducing a specific group-membership shape (including nested groups) for a bug repro
Supported schemes¶
Basic and Forms only. Requests using NTLM, Bearer, Certificate, or Anonymous schemes are
rejected (Fail) so this provider never shadows the dedicated provider for those schemes.
Quick Start¶
Config lives at Cesivi:Identity:Providers:ConfigUsers. Users and groups can be defined
inline in appsettings.json, or in an external JSON file (set UsersFile, resolved
relative to the app's content root) — external file wins over inline when both are set. This
makes it easy to swap a fixture-users file per test environment without editing appsettings.
{
"Cesivi": {
"Identity": {
"Providers": {
"ConfigUsers": {
"Enabled": true,
"Priority": 75,
"UsersFile": null,
"Users": [
{
"Username": "alice",
"Password": "s3cret",
"DisplayName": "Alice Anderson",
"Email": "alice@test.local",
"Domain": "DOMAIN",
"Groups": [ "Owners" ]
},
{
"Username": "bob",
"Password": "hunter2",
"DisplayName": "Bob Baker",
"Email": "bob@test.local",
"Domain": "DOMAIN",
"Groups": [ "Members" ]
}
],
"Groups": [
{ "Name": "Owners", "DisplayName": "Site Owners" },
{ "Name": "Members", "DisplayName": "Site Members" }
]
}
}
}
}
}
Field reference:
| Field | Meaning |
|---|---|
Enabled |
Master switch. false (default) means the provider isn't even registered. |
Priority |
Default 75 — sits between OAuth2 (50) and NTLM (100), so a real Bearer/NTLM login still wins, but a Basic credential matching a config user beats AcceptAll (1000). |
UsersFile |
Optional path to an external JSON file with the same Users/Groups shape. Wins over inline lists when set. |
Users[].Domain |
Optional NT-style domain. When set, the login name (and SPUser LoginName) becomes DOMAIN\username; when empty, just username. |
Users[].Groups |
Direct group memberships (by group Name). Transitive membership through nested groups is resolved at load time. |
Groups[].Members |
A group's member list may reference users or other groups by name — the loader classifies each entry after loading. Nested groups resolve transitively. |
Nested groups & cycle detection¶
Groups can contain other groups (e.g. AllStaff containing Owners and Members). The loader
builds the full membership graph at startup and validates it's acyclic — a cyclic group
definition (e.g. A contains B contains A) makes the server refuse to start, with the
exact cycle path in the error. This is a fail-fast config error, not a runtime fallback.
Example Usage¶
# Basic auth against a ConfigUsers-defined user
curl -u "alice:s3cret" http://localhost:5000/_api/web
# PnP PowerShell with a ConfigUsers credential
$cred = Get-Credential # username: alice, password: s3cret
Connect-CSOnline -Url http://localhost:5000 -Credentials $cred
Source: Cesivi.Common/Identity/Providers/ConfigUsers/ · sample config:
Cesivi.Server/appsettings.json (Cesivi:Identity:Providers:ConfigUsers, disabled by default).
TrustedCaller Provider¶
Identity delegation for trusted applications like Cesivi WebUI. Allows a front-end application to authenticate users locally and pass that identity securely to SPM for authorization.
When to Use¶
- Cesivi WebUI
- Service-to-service authentication
- Custom front-end applications
- Scenarios requiring identity delegation
How It Works¶
- WebUI authenticates user (via its own login page)
- WebUI requests a challenge from SPM (
/_spmock/trust/challenge) - WebUI signs the challenge with its private key + user identity
- SPM validates signature using the trusted caller's public key
- SPM accepts delegated identity for authorization decisions
This certificate-based challenge-response protocol prevents replay attacks and ensures only trusted callers can delegate identities.
Quick Start¶
- Generate certificates using the provided script
- Configure WebUI with certificate path and password
- Configure SPM with certificate thumbprint
- Enable the trusted caller in SPM
# Generate certificates
cd Scripts
.\New-TrustCertificate.ps1
Configuration¶
SPM Server (appsettings.json):
{
"Cesivi": {
"TrustedCallers": [
{
"CallerId": "webui-001",
"CertificateThumbprint": "<thumbprint from script>",
"AllowedScopes": ["read", "write", "admin"],
"Description": "Cesivi WebUI",
"Enabled": true
}
]
}
}
WebUI (appsettings.json):
{
"SpmClient": {
"BaseUrl": "http://localhost:5000",
"TrustCertificatePath": "certificates/webui-001.pfx",
"TrustCertificatePassword": "<password from script>",
"TrustedCallerId": "webui-001"
}
}
Security Features¶
- Challenge expiration: 5 minutes (prevents replay attacks)
- Challenge single-use: Each challenge can only be used once
- Identity timestamp: Delegated identity valid for 5 minutes
- Certificate-based signing: RSA SHA256 signatures
- Priority 10: Processed before other providers
See: WebUI Setup Guide for detailed configuration
Provider Priority¶
Cesivi tries providers in priority order (lower number = higher priority):
| Priority | Provider | Why This Order? |
|---|---|---|
| 10 | TrustedCaller | Highest - trusted apps with signed delegation |
| 50 | OAuth2/OIDC | High specificity - only handles Bearer tokens |
| 75 | ConfigUsers | Between OAuth2 and NTLM - a real Bearer/NTLM login still wins first |
| 100 | NTLM | Medium specificity - handles NTLM/Negotiate |
| 1000 | AcceptAll | Fallback - accepts everything |
This ensures: - Trusted caller headers go to TrustedCaller provider (signed delegation) - Bearer tokens go to OAuth2 provider (JWT validation) - Config-defined Basic/Forms users go to ConfigUsers before falling through to NTLM/AcceptAll - NTLM requests go to NTLM provider (challenge-response) - Everything else falls back to AcceptAll (development mode)
Enabling Multiple Providers¶
You can enable multiple providers simultaneously. The server will try them in priority order:
{
"Cesivi": {
"Identity": {
"Providers": {
"OAuth2": {
"Enabled": true,
"Priority": 50
},
"NTLM": {
"Enabled": true,
"Priority": 100
},
"AcceptAll": {
"Enabled": true,
"Priority": 1000
}
}
}
}
}
Flow: 1. Request arrives with Bearer token → OAuth2 validates JWT 2. Request arrives with NTLM header → NTLM does challenge-response 3. Request arrives with Basic auth → Falls back to AcceptAll
Security Considerations¶
AcceptAll Provider¶
⚠️ NOT FOR PRODUCTION
- Accepts any credentials without validation
- No actual security
- Use only for development/testing
OAuth2/OIDC Provider¶
✅ Production Ready
- Full JWT signature validation
- Token expiration checking
- Issuer and audience validation
- Configurable clock skew (default: 5 minutes)
Best Practices: - Use HTTPS in production - Rotate signing keys regularly - Set appropriate token lifetimes - Validate audience claims
NTLM Provider¶
✅ Production Ready (with AD backend)
- Full challenge-response protocol
- Configuration mode: Passwords stored in appsettings (⚠️ secure your config!)
- AD/LDAP mode: Delegates to Active Directory
Best Practices: - Use encrypted configuration (User Secrets, Azure Key Vault) - Enable SSL/TLS for LDAP connections - Use service accounts with minimal privileges
Troubleshooting¶
"Authentication failed" with OAuth2¶
Check:
1. Token is not expired (exp claim)
2. Audience matches configuration (aud claim)
3. Issuer matches configuration (iss claim)
4. OIDC discovery URL is accessible ({Authority}/.well-known/openid-configuration)
Solution: Enable debug logging to see JWT validation errors
{
"Logging": {
"LogLevel": {
"Cesivi.Common.Identity": "Debug"
}
}
}
"NTLM authentication failed"¶
Check: 1. Username/password correct in configuration 2. Domain name matches exactly (case-sensitive) 3. NTLM provider enabled
Solution: Check logs for detailed NTLM negotiation steps
"All providers rejected request"¶
Check: 1. At least one provider is enabled 2. AcceptAll is enabled as fallback 3. Authorization header is present
Solution: Enable AcceptAll as fallback during development
Migration Guide¶
From AcceptAll to OAuth2¶
- Set up your identity provider (Azure AD, Okta, etc.)
- Enable OAuth2 in appsettings.json with your configuration
- Keep AcceptAll enabled initially (for testing)
- Test OAuth2 authentication with real tokens
- Disable AcceptAll once OAuth2 is working
From AcceptAll to NTLM¶
- Define users in configuration or set up AD/LDAP backend
- Enable NTLM in appsettings.json
- Keep AcceptAll enabled initially (for testing)
- Test NTLM authentication with valid credentials
- Disable AcceptAll once NTLM is working
Advanced Configuration¶
Custom Priorities¶
You can change provider priorities to control the order:
{
"Cesivi": {
"Identity": {
"Providers": {
"NTLM": {
"Priority": 10
},
"OAuth2": {
"Priority": 20
},
"AcceptAll": {
"Priority": 1000
}
}
}
}
}
Disabling Providers¶
Set "Enabled": false to disable a provider:
{
"Cesivi": {
"Identity": {
"Providers": {
"AcceptAll": {
"Enabled": false
}
}
}
}
}
See Also¶
- OAuth2 Setup Guide - Detailed OAuth2/OIDC configuration
- NTLM Setup Guide - Detailed NTLM configuration
- WebUI Setup Guide - WebUI deployment and trust configuration
- API Reference - Authentication endpoints
- Troubleshooting Guide - Common authentication issues