hendryanw
hendryanw

Reputation: 1937

How to use PowerShell to open and receive response from authorization dialog of Azure Active Directory OAuth Authorization Code Grant Flow

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

Answers (1)

Sa Yang
Sa Yang

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

Related Questions