Private/Get-SamAuthorizationCode.ps1

function Get-SAMAuthorizationCode {
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    # .SYNOPSIS
    # Gets an authorization code and token from Microsoft Partner Center using OAuth 2.0 authorization code flow.
    #
    # .DESCRIPTION
    # This function initiates an OAuth 2.0 authorization code flow to obtain an access token for Microsoft Partner Center.
    # It opens a browser window for user authentication, starts a local HTTP listener to receive the callback,
    # and exchanges the authorization code for an access token.
    #
    # .PARAMETER tenantId
    # The Microsoft Partner Center tenant ID (MSP Tenant ID)
    #
    # .PARAMETER appId
    # The application ID of the SAM application (client ID) registered in Entra AD
    #
    # .PARAMETER redirectUri
    # The redirect URI configured for the application (default: http://localhost:8400)
    #
    # .PARAMETER scope
    # The requested scope for the access token (default: https://api.partnercenter.microsoft.com/.default)
    #
    # .EXAMPLE
    # $params = @{
    # tenantId = "12345678-1234-1234-1234-123456789012"
    # appId = "87654321-4321-4321-4321-210987654321"
    # redirectUri = "http://localhost:8400"
    # scope = "https://api.partnercenter.microsoft.com/.default"
    # }
    # $token = Get-SAMAuthorizationCode @params
    #
    # .EXAMPLE
    # # Using SAM configuration data
    # $samConfig = (Get-ConfigData).SAM
    # $token = Get-SAMAuthorizationCode -tenantId $samConfig.MSPTenantId -appId $samConfig.ApplicationId -appSecret $samConfig.ApplicationSecret
    #
    # .NOTES
    # The function requires the System.Web assembly for URL parsing.
    # Make sure the redirect URI matches what is configured in your Azure AD application.

    param (
        [string]$tenantId, #MSPTenantId
        [string]$appId, #SAMApplicationId
        [string]$appSecret, #SAMApplicationSecret
        [string]$redirectUri = 'http://localhost:8400',
        [string]$scope = 'https://api.partnercenter.microsoft.com/.default'
    )
    
    # Add assembly for HttpUtility
    Add-Type -AssemblyName System.Web
    
    # Create HTTP Listener
    $listener = $null
    try {
        $listener = New-Object System.Net.HttpListener
        $listener.Prefixes.Add("http://localhost:8400/")
        $listener.Start()

        # Construct auth URL
        $authEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/authorize?" + `
            "client_id=$appId&" + `
            "response_type=code&" + `
            "redirect_uri=$redirectUri&" + `
            "scope=$scope"

        # Open default browser
        Start-Process $authEndpoint

        # Wait for the callback
        $context = $listener.GetContext()
        $requestUrl = $context.Request.Url
        
        # Send a response to close the browser window
        $response = $context.Response
        $responseString = "<html><body><h1>Authorization complete. You can close this window.</h1></body></html>"
        $buffer = [System.Text.Encoding]::UTF8.GetBytes($responseString)
        $response.ContentLength64 = $buffer.Length
        $response.OutputStream.Write($buffer, 0, $buffer.Length)
        $response.Close()

        # Extract the authorization code and get token
        $code = [System.Web.HttpUtility]::ParseQueryString($requestUrl.Query)["code"]
        
        $body = "grant_type=authorization_code&client_id=$appId&client_secret=$appSecret&code=$code&redirect_uri=$redirectUri&scope=$scope"
        $headers = @{ 'Content-Type' = 'application/x-www-form-urlencoded' }
        $tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/token"
        $response = Invoke-RestMethod -Method POST -Uri $tokenEndpoint -Body $body -Headers $headers

        $AccessToken = @{ Authorization = "Bearer $($response.Access_Token)" }
        return $AccessToken
    }
    catch {
        Write-Error "Authorization failed: $_"
        throw
    }
    finally {
        if ($null -ne $listener -and $listener.IsListening) {
            $listener.Stop()
            $listener.Close()
        }
    }
}