Reputation: 1937
I am trying to write a PowerShell script that let users to authorize an Azure Active Directory application to act on their behalf.
Following the Authorization Code Grant flow documentation from Microsoft. I am invoking the Authorization endpoint with the following line:
Invoke-WebRequest -Method GET -Uri "https://login.microsoftonline.com/$tenantId/oauth2/authorize?client_id=$applicationId&response_type=code&redirect_uri=$redirectUri&response_mode=query&resource=$resource&state=09876"
It simultaneously return a response in PowerShell and open a web page on my default browser at the same time.
Below is what the response looks like:
StatusCode : 200
StatusDescription : OK
Content :
<!DOCTYPE html>
<html dir="ltr" class="" lang="en">
<head>
<title>Sign in to your account</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-eq...
RawContent : HTTP/1.1 200 OK
Pragma: no-cache
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
x-ms-request-id: ed3ee9de-ccc4-47ea-ac52-087b...
Forms : {}
Headers : {[Pragma, no-cache], [Strict-Transport-Security, max-age=31536000; includeSubDomains],
[X-Content-Type-Options, nosniff], [X-Frame-Options, DENY]...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 22592
The web page displayed in the browser is redirected to https://login.microsoftonline.com/cookiesdisabled
with the following message displayed:
We can't sign you in Your browser is currently set to block cookies. You need to allow cookies to use this service. Cookies are small text files stored on your computer that tell us when you're signed in. To learn how to allow cookies, check the online help in your web browser.
So to summarize, it does not work!
Note that, cookies is enabled in my browser, and if I invoke the web request from the web browser, it will work just fine.
What am I doing wrong? and how do I prompt the dialog to the user using PowerShell and receive the authorization code response back in the PowerShell?
Thank you.
Upvotes: 5
Views: 3958
Reputation: 9401
You're right. This is becuasue that Poweshell cannot catch the authorization code that comes back with the Reply URL.
Solution:
You can write a login brower mudule to act as a "fake" endpoint for the authorization code that comes back with the Reply URL.
Try to use this Example :
LoginBrowser
Add-Type -AssemblyName System.Web
$outputAuth = ".\Code.txt"
$outputError = ".\Error.txt"
function LoginBrowser
{
param
(
[Parameter(HelpMessage='Authorization URL')]
[ValidateNotNull()]
[string]$authorizationUrl,
[Parameter(HelpMessage='Redirect URI')]
[ValidateNotNull()]
[uri]$redirectUri
)
# Create an Internet Explorer Window for the Login Experience
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Width = 600
$ie.Height = 500
$ie.AddressBar = $false
$ie.ToolBar = $false
$ie.StatusBar = $false
$ie.visible = $true
$ie.navigate($authorzationUrl)
while ($ie.Busy) {}
:loop while($true)
{
# Grab URL in IE Window
$urls = (New-Object -ComObject Shell.Application).Windows() | Where-Object {($_.LocationUrl -match "(^https?://.+)|(^ftp://)") -and ($_.HWND -eq $ie.HWND)} | Where-Object {$_.LocationUrl}
foreach ($a in $urls)
{
# If URL is in the form we expect, with the Reply URL as the domain, and the code in the URL, grab the code
if (($a.LocationUrl).StartsWith($redirectUri.ToString()+"?code="))
{
$code = ($a.LocationUrl)
($code = $code -replace (".*code=") -replace ("&.*")) | Out-File $outputAuth
break loop
}
# If we catch an error, output the error information
elseif (($a.LocationUrl).StartsWith($redirectUri.ToString()+"?error="))
{
$error = [System.Web.HttpUtility]::UrlDecode(($a.LocationUrl) -replace (".*error="))
$error | Out-File $outputError
break loop
}
}
}
# Return the Auth Code
return $code
}
REST with AUTHCODE
# Load ADAL
Add-Type -Path "..\ADAL\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
# Load our Login Browser Function
Import-Module ./LoginBrowser.psm1
# Output Token and Response from AAD Graph API
$accessToken = ".\Token.txt"
$output = ".\Output.json"
# Application and Tenant Configuration
$clientId = "<AppIDGUID>"
$tenantId = "<TenantID>"
$resourceId = "https://graph.windows.net"
$redirectUri = New-Object system.uri("<ReplyURL>")
$login = "https://login.microsoftonline.com"
# Create Client Credential Using App Key
$secret = "<AppKey>"
# Create Client Credential Using Certificate
#$certFile = "<PFXFilePath>"
#$certFilePassword = "<CertPassword>"
#$secret = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate -ArgumentList $certFile,$certFilePassword
# Note you can adjust the querystring paramters here to change things like prompting for consent
$authorzationUrl = ("{0}/{1}/oauth2/authorize?response_type=code&client_id={2}&redirect_uri={3}&resource={4}&prompt=consent" -f $login,$tenantId,$clientId,$redirectUri,$resourceId)
# Fake a proper endpoint for the Redirect URI
$code = LoginBrowser $authorzationUrl $redirectUri
# Get an Access Token with ADAL
$clientCredential = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential($clientId,$secret)
$authContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("{0}/{1}" -f $login,$tenantId)
$authenticationResult = $authContext.AcquireToken($resourceId, $clientcredential)
($token = $authenticationResult.AccessToken) | Out-File $accessToken
# Call the AAD Graph API
$headers = @{
"Authorization" = ("Bearer {0}" -f $token);
"Content-Type" = "application/json";
}
# Output response as JSON file
Invoke-RestMethod -Method Get -Uri ("{0}/{1}/users?api-version=1.6" -f $resourceId,$tenantId) -Headers $headers -OutFile $output
Upvotes: 3