Splunk Enterprise

Azure AD Authentication URLs Returning 200 Response Code Even with Incorrect Credentials

Devika_20
New Member

We are using the following PowerShell script to monitor Azure AD authentication-enabled URLs in Splunk. However, when incorrect credentials are entered, a 200 response code is returned instead of the expected failure response (e.g., 401 Unauthorized).

Has anyone encountered this issue? Please help us rectify this and ensure that incorrect credentials are flagged with the appropriate response code.

# Prompt User for Credentials
$credential = Get-Credential

 

# Define Target URL
$targetUrl = "<TARGET_URL>"  # URL to monitor

 

# Convert Credentials to Base64 for Authorization Header
$username = $credential.UserName
$password = $credential.GetNetworkCredential().Password
$authValue = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$username`:$password"))
$headers = @{ Authorization = "Basic $authValue" }

 

# Send Request with Authorization
try {
    $response = Invoke-WebRequest -Uri $targetUrl -Headers $headers -Method Get -UseBasicParsing -ErrorAction Stop

 

    # Check if the server actually challenges for authentication
    if ($response.StatusCode -eq 200 -and $response.Headers["WWW-Authenticate"]) {
        Write-Host "Authentication failed: Invalid credentials provided."
    } else {
        Write-Host "Response Code: $($response.StatusCode)"
    }
} catch {
    if ($_.Exception.Response.StatusCode -eq 401) {
        Write-Host "Authentication failed: Invalid credentials provided."
    } else {
        Write-Host "Request failed with error: $($_.Exception.Message)"
    }
}

0 Karma

livehybrid
SplunkTrust
SplunkTrust

Hi @Devika_20 

I think Azure AD-protected URL expects (OAuth 2.0 / OpenID Connect) authentication rather than Basic auth, its worth reviewing the docs on these URLs.

Regarding the 200 status:

Unauthenticated requests to Azure AD-protected resources usually trigger a redirection (HTTP 302) to the Microsoft login page (login.microsoftonline.com).

Invoke-WebRequest might receive this 302 redirect. By default, it might try to follow it. However, the ultimate login page requires interactive user input (or specific OAuth flows), which your script isn't performing.

It's also possible that the initial response before the redirect, or the response at the redirect URL itself if redirects aren't followed correctly, is interpreted as a 200 OK by Invoke-WebRequest, especially if -UseBasicParsing simplifies how responses are handled. The server isn't explicitly rejecting the credentials with a 401 because it wasn't even trying to process them via Basic Auth; it was trying to initiate the standard Azure AD login flow.

The other thing to check is the logic around the 200 status and WWW-Authenticate headers, I believe the check if ($response.StatusCode -eq 200 -and $response.Headers["WWW-Authenticate"]) inside the try block is incorrect. A 200 OK response signifies success. The WWW-Authenticate header is typically sent with a 401 Unauthorized response to tell the client how to authenticate, not upon success. This condition would likely never be true in a standard scenario.

Ultimately correct way to achieve this would be using OAuth

You would need to modify your script to authenticate using an OAuth 2.0 flow appropriate for a non-interactive script. The Client Credentials Flow is the standard and most secure method for service-to-service or script-based authentication against Azure AD.

 

Steps:

  1. Azure AD App Registration:

    • Register an application in your Azure AD tenant.
    • Under "API permissions," grant this application the necessary permissions to access your target URL/API (e.g., if it's a custom API, grant application permissions defined by that API). Make sure to grant admin consent if required.
    • Under "Certificates & secrets," create a new client secret. Copy this secret value immediately, as you won't be able to see it again.
    • Note down the Application (client) ID and the Directory (tenant) ID.
  2. Modify the PowerShell Script: Replace the Basic Auth logic with OAuth 2.0 Client Credentials Flow.
    The following template would be a good starting point:

# --- Configuration ---
$clientId = "YOUR_APP_REGISTRATION_CLIENT_ID"
$clientSecret = "YOUR_APP_REGISTRATION_CLIENT_SECRET"  # Consider using Azure Key Vault or secure storage
$tenantId = "YOUR_AZURE_AD_TENANT_ID"
$targetUrl = "<TARGET_URL>" # URL to monitor

# Define the scope. Often 'https://resource.example.com/.default' or 'api://<api_client_id>/.default'
# For Microsoft Graph API it might be 'https://graph.microsoft.com/.default'
# Check the documentation for your specific targetUrl API or Azure service
$scope = "YOUR_API_SCOPE/.default" # e.g., "api://xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/.default" or resource URI + "/.default"

# --- Get Access Token ---
$tokenRequestBody = @{
    Grant_Type    = "client_credentials"
    Scope         = $scope
    Client_Id     = $clientId
    Client_Secret = $clientSecret
}

$tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"

try {
    Write-Verbose "Requesting Access Token from $tokenEndpoint"
    $tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenEndpoint -Body $tokenRequestBody -ContentType 'application/x-www-form-urlencoded'
    $accessToken = $tokenResponse.access_token
    Write-Verbose "Successfully obtained Access Token."
} catch {
    Write-Host "Error obtaining Access Token: $($_.Exception.Message)"
    # Log detailed error for Splunk
    Write-Host "OAuth Token Request Failed. Status Code: $($_.Exception.Response.StatusCode). Response Body: $($_.Exception.Response.GetResponseStream() | Foreach-Object { New-Object System.IO.StreamReader($_) } | Foreach-Object { $_.ReadToEnd() })"
    exit 1 # Exit because we cannot proceed without a token
}

# --- Send Request with Bearer Token ---
$headers = @{ Authorization = "Bearer $accessToken" }

try {
    Write-Verbose "Sending request to $targetUrl"
    # -MaximumRedirection 0 prevents following redirects which might mask the initial auth status
    $response = Invoke-WebRequest -Uri $targetUrl -Headers $headers -Method Get -UseBasicParsing -ErrorAction Stop -MaximumRedirection 0

    # Successful request (usually 2xx)
    # Log success for Splunk
    Write-Host "Response Code: $($response.StatusCode)"
    Write-Host "Monitoring check successful for $targetUrl"

} catch {
    # Handle HTTP errors (like 401 Unauthorized, 403 Forbidden, etc.)
    $statusCode = $_.Exception.Response.StatusCode
    $statusDescription = $_.Exception.Response.StatusDescription

    # Log failure for Splunk
    Write-Host "Response Code: $statusCode"
    Write-Host "Status Description: $statusDescription"
    Write-Host "Monitoring check failed for $targetUrl. Error: $($_.Exception.Message)"

    # You can add specific logging/alerting for critical codes like 401/403
    if ($statusCode -eq [System.Net.HttpStatusCode]::Unauthorized -or $statusCode -eq [System.Net.HttpStatusCode]::Forbidden) {
        Write-Host "CRITICAL: Authentication or Authorization failed ($statusCode)."
        # Add specific Splunk logging for auth failure here
    }
    # Optional: Log response body for debugging, be careful with sensitive data
    # $errorResponseBody = $_.Exception.Response.GetResponseStream() | Foreach-Object { New-Object System.IO.StreamReader($_) } | Foreach-Object { $_.ReadToEnd() }
    # Write-Host "Error Response Body: $errorResponseBody"
}

Please let me know how you get on and consider adding karma to this or any other answer if it has helped.
Regards

Will

0 Karma
Career Survey
First 500 qualified respondents will receive a $20 gift card! Tell us about your professional Splunk journey.

Can’t make it to .conf25? Join us online!

Get Updates on the Splunk Community!

Can’t Make It to Boston? Stream .conf25 and Learn with Haya Husain

Boston may be buzzing this September with Splunk University and .conf25, but you don’t have to pack a bag to ...

Splunk Lantern’s Guide to The Most Popular .conf25 Sessions

Splunk Lantern is a Splunk customer success center that provides advice from Splunk experts on valuable data ...

Unlock What’s Next: The Splunk Cloud Platform at .conf25

In just a few days, Boston will be buzzing as the Splunk team and thousands of community members come together ...