Authentication Guide
Secure your PoshMcp server with either Azure Entra ID (OAuth 2.1) or API key authentication.
Use this page to choose the right option for your deployment and configure it with the current Authentication schema.
Choose an Authentication Mode
- Use Entra ID (OAuth 2.1) for enterprise identity, centralized access control, token issuance, and browser/client compatibility. Start with Entra ID (OAuth 2.1).
- Use API key authentication for internal services, automation, and simple server-to-server access where key distribution is acceptable. Start with API Key Authentication.
- In Azure-hosted production, a common pattern is Entra ID for inbound client authentication plus Managed Identity for outbound Azure resource access.
Quick Comparison
| Aspect | App Registration | Managed Identity | Best For |
|---|---|---|---|
| Defines OAuth scopes? | ✓ Yes (required) | ✗ No | OAuth API definition |
| Provides server credentials? | ✓ Yes (client secret) | ✓ Yes (automatic) | Server calling Azure APIs |
| Setup complexity | Medium (portal steps) | Low (enable on resource) | Ease of deployment |
| Secret rotation burden? | High (manual) | None (automatic) | Reduced ops overhead |
| Works on-premises? | ✓ Yes | ✗ No (requires Azure compute) | Non-Azure deployments |
| Works in Azure Container Apps/AKS? | ✓ Yes | ✓ Yes | Azure-native deployments |
Decision Matrix
- On-premises or hybrid: Use App Registration only
- Azure Container Apps, AKS, or App Service: Use Managed Identity + App Registration (recommended)
- Simple testing: Use App Registration only
- Enterprise with compliance: Use both for maximum flexibility
Entra ID (OAuth 2.1)
Use Entra ID when clients should present bearer tokens and you need centralized identity lifecycle management.
App Registration
Use App Registration to define OAuth scopes and control client access. Works anywhere.
Setup in Azure Portal
- Go to Azure Portal → Azure Active Directory → App registrations → New registration
- Name:
PoshMcp Server - Click Register
Save these values:
- Application (client) ID
- Directory (tenant) ID
Expose API Scopes
- Go to Expose an API
- Set Application ID URI to
api://poshmcp-prod(or custom value) - Click Add a scope
- Scope name:
access_as_server - Fill in consent prompts and save
Configure PoshMcp
Edit appsettings.json:
{
"Authentication": {
"Enabled": true,
"DefaultScheme": "Bearer",
"DefaultPolicy": {
"RequireAuthentication": true,
"RequiredScopes": ["api://poshmcp-prod/access_as_server"]
},
"Schemes": {
"Bearer": {
"Type": "JwtBearer",
"Authority": "https://login.microsoftonline.com/{tenant-id}",
"Audience": "api://poshmcp-prod",
"RequireHttpsMetadata": true,
"ValidIssuers": ["https://login.microsoftonline.com/{tenant-id}/v2.0"]
}
},
"ProtectedResource": {
"Resource": "api://poshmcp-prod",
"ResourceName": "PoshMcp Server",
"AuthorizationServers": ["https://login.microsoftonline.com/{tenant-id}"],
"ScopesSupported": ["api://poshmcp-prod/access_as_server"],
"BearerMethodsSupported": ["header"]
}
},
"PowerShellConfiguration": {
"CommandNames": ["Get-Process", "Get-Service"]
}
}
Replace {tenant-id} with your Directory (tenant) ID from the app registration.
Managed Identity (Azure-Hosted Deployments)
Use managed identity when PoshMcp runs on Azure compute (Container Apps, AKS, App Service). Eliminates credential management on the server.
Enable Managed Identity
Azure Container Apps:
az containerapp create \
--name poshmcp \
--resource-group MyResourceGroup \
--image myregistry.azurecr.io/poshmcp:latest \
--system-assigned
Azure Kubernetes Service (AKS):
Use workload identity for pods:
apiVersion: v1
kind: ServiceAccount
metadata:
name: poshmcp
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: poshmcp
spec:
template:
spec:
serviceAccountName: poshmcp
containers:
- name: poshmcp
image: myregistry.azurecr.io/poshmcp:latest
Grant Permissions
Get the managed identity's principal ID and grant access to services:
principalId=$(az containerapp identity show --name poshmcp --resource-group MyResourceGroup --query principalId -o tsv)
# Grant Key Vault access
az keyvault set-policy \
--name mykeyvault \
--object-id $principalId \
--secret-permissions get list
# Or grant storage access
az role assignment create \
--assignee $principalId \
--role "Storage Blob Data Reader" \
--scope /subscriptions/{subscription}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/mystorageacct
Configuration
Use the same appsettings.json format as App Registration. The difference is credentials come from the Azure platform automatically.
API Key Authentication
Use API key authentication when you need lightweight authentication for trusted clients, internal automation, or bootstrap scenarios.
Edit appsettings.json:
{
"Authentication": {
"Enabled": true,
"DefaultScheme": "ApiKey",
"DefaultPolicy": {
"RequireAuthentication": true,
"RequiredScopes": [],
"RequiredRoles": ["reader"]
},
"Schemes": {
"ApiKey": {
"Type": "ApiKey",
"HeaderName": "X-API-Key",
"Keys": {
"key-reader": {
"Scopes": [],
"Roles": ["reader"]
},
"key-ops": {
"Scopes": [],
"Roles": ["ops", "reader"]
}
}
}
}
}
}
Make an authenticated request:
curl -H "X-API-Key: key-reader" https://poshmcp.example.com/tools
For per-tool authorization precedence with PowerShellConfiguration.FunctionOverrides, see Configuration Guide.
Testing Authentication
Check Protected Resource Metadata
curl https://poshmcp.example.com/.well-known/oauth-protected-resource
Expected response:
{
"resource": "api://poshmcp-prod",
"resource_name": "PoshMcp Server",
"authorization_servers": ["https://login.microsoftonline.com/{tenant-id}"],
"scopes_supported": ["api://poshmcp-prod/access_as_server"],
"bearer_methods_supported": ["header"]
}
Acquire a Token
curl -X POST \
https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token \
-d "client_id={client-id}" \
-d "client_secret={client-secret}" \
-d "scope=api://poshmcp-prod/.default" \
-d "grant_type=client_credentials"
Make Authenticated Request
curl -H "Authorization: Bearer {token}" \
https://poshmcp.example.com/tools
Security Best Practices
1. Use HTTPS
Always use HTTPS for production. Entra ID requires it.
2. Design Scopes for Least Privilege
Create granular scopes instead of "admin" or "full access":
{
"scopes_supported": [
"api://poshmcp-prod/read.diagnostics",
"api://poshmcp-prod/execute.storage",
"api://poshmcp-prod/execute.compute"
]
}
3. Combine with Command Allowlists
{
"PowerShellConfiguration": {
"CommandNames": [
"Get-Process",
"Get-Service"
],
"ExcludePatterns": [
"Remove-*",
"*-Credential",
"Invoke-Expression"
]
}
}
4. Store Secrets in Key Vault
Never embed secrets in appsettings.json:
az keyvault secret set --vault-name MyKeyVault --name poshmcp-client-secret --value "..."
Reference in Container Apps:
az containerapp secret set --name poshmcp --secrets client-secret=keyvaultref:MyKeyVault/poshmcp-client-secret
5. Enable Audit Logging
PoshMcp logs user identity and command execution:
export POSHMCP_LOG_LEVEL=Information
Logs include:
- User principal name (from token)
- Command executed
- Correlation ID (trace requests across systems)
Troubleshooting
401 Unauthorized on every request
Check:
- Is authentication enabled in
appsettings.json? - Are you sending
Authorization: Bearer {token}header? - Is the token expired? Check the
expclaim.
Invalid token signature
Check:
- Did you acquire the token from the correct Entra ID tenant?
- Is the server using HTTPS? (Entra ID requires it for production)
- Do the token's
iss,aud, andscpclaims match your configuration?
Decode the token at jwt.io to inspect claims.
Insufficient permissions / scope mismatch
Error: The claim 'scp' does not contain any of the required values
Fix:
- Verify the token includes the required scope (decode and check
scpclaim) - In Entra ID, verify the scope is defined in "Expose an API"
- Verify the client requested the correct scope when acquiring the token
- Check
DefaultPolicy.RequiredScopesinappsettings.json
Managed Identity: IMDS endpoint not found
Symptoms: Can't access Azure services from PoshMcp despite managed identity being enabled.
Fix:
- Verify managed identity is enabled on your compute resource
- Check for network policies or proxies blocking access to
169.254.169.254 - Allow IMDS (Instance Metadata Service) access:
# Inside container, test IMDS curl -H "Metadata:true" http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-12-01&resource=https://management.azure.com
Managed Identity: Access denied to Azure service
Fix: Grant the managed identity the appropriate role:
principalId=$(az containerapp identity show --name poshmcp --resource-group MyResourceGroup --query principalId -o tsv)
az role assignment create \
--assignee $principalId \
--role "Key Vault Secrets User" \
--scope /subscriptions/{subscription}/resourceGroups/{rg}/providers/Microsoft.KeyVault/vaults/mykeyvault
Next: Security Best Practices | Configuration Guide | Docker Deployment