Question:
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:
1 2 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
StatusCode : 200 StatusDescription : OK Content : 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.
Answer:
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# 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 = " $tenantId = " $resourceId = "https://graph.windows.net" $redirectUri = New-Object system.uri(" $login = "https://login.microsoftonline.com" # Create Client Credential Using App Key $secret = " # Create Client Credential Using Certificate #$certFile = " #$certFilePassword = " #$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 |