Authentication.psm1

<#
.SYNOPSIS
    This function retrieves the Tech Data API headers using admin account credentials or a MSPComplete Endpoint.
.DESCRIPTION
    This function retrieves the Tech Data API request headers using admin account credentials or a MSPComplete Endpoint.
    It returns the request headers if successful, null otherwise.
.PARAMETER url
    The Tech Data authentication URL.
.PARAMETER clientID
    The Tech Data client ID.
.PARAMETER clientSecret
    The Tech Data client secret.
.PARAMETER grantType
    The Tech Data grant type.
.PARAMETER soin
    The Tech Data StreamOne Identification Number (SOIN)
.PARAMETER endpoint
    The MSPComplete Endpoint containing the Tech Data authentication URL as its "Url" property, the
    Tech Data client ID, client secret and grant type, and SOIN as
    "ClientID", "ClientSecret", "GrantType" and "SOIN" extended properties
.EXAMPLE
    Get-TechDataAccessToken -Endpoint $Endpoint
.EXAMPLE
    $Endpoint | Get-TechDataAccessToken
.EXAMPLE
    Get-TechDataAccessToken -Url $url -ClientID $clientID -ClientSecret $clientSecret -GrantType $grantType -Soin $soin
#>

function Get-TechDataApiHeaders {
    param (
        # The Tech Data authentication URL.
        [Parameter(Mandatory=$true, ParameterSetName="credential")]
        [String]$url,

        # The Tech Data client ID.
        [Parameter(Mandatory=$true, ParameterSetName="credential")]
        [String]$clientID,

        # The Tech Data client secret.
        [Parameter(Mandatory=$true, ParameterSetName="credential")]
        [String]$clientSecret,

        # The Tech Data grant type.
        [Parameter(Mandatory=$true, ParameterSetName="credential")]
        [String]$grantType,

        # The Tech Data StreamOne Identification Number (SOIN)
        [Parameter(Mandatory=$true, ParameterSetName="credential")]
        [String]$soin,

        # The MSPComplete Endpoint containing the Tech Data authentication URL as its "Url" property, the
        # Tech Data client ID, client secret and grant type, and SOIN as
        # "ClientID", "ClientSecret", "GrantType" and "SOIN" extended properties
        [Parameter(Mandatory=$true, ParameterSetName="endpoint", ValueFromPipeline=$true)]
        $endpoint
    )

    try {
        # If given endpoint, retrieve values for authentication
        if ($PSCmdlet.ParameterSetName -eq "endpoint") {
            # Retrieve authentication URL
            if ([String]::IsNullOrWhiteSpace($endpoint.Configuration.Url)) {
                Write-Error "Endpoint has null/empty 'Url' property."
                return $null
            }
            $url = $endpoint.Configuration.Url

            # Retrieve client ID
            if ([String]::IsNullOrWhiteSpace($endpoint.ExtendedProperties.ClientID)) {
                Write-Error "Endpoint has null/empty 'ClientID' extended property."
                return $null
            }
            $clientID = $endpoint.ExtendedProperties.ClientID

            # Retrieve client secret
            if ([String]::IsNullOrWhiteSpace($endpoint.ExtendedProperties.ClientSecret)) {
                Write-Error "Endpoint has null/empty 'ClientSecret' extended property."
                return $null
            }
            $clientSecret = $endpoint.ExtendedProperties.ClientSecret

            # Retrieve grant type
            if ([String]::IsNullOrWhiteSpace($endpoint.ExtendedProperties.GrantType)) {
                Write-Error "Endpoint has null/empty 'GrantType' extended property."
                return $null
            }
            $grantType = $endpoint.ExtendedProperties.GrantType

            # Retrieve SOIN
            if ([String]::IsNullOrWhiteSpace($endpoint.ExtendedProperties.SOIN)) {
                Write-Error "Endpoint has null/empty 'SOIN' extended property."
                return $null
            }
            $soin = $endpoint.ExtendedProperties.SOIN
        }

        # Construct call to authentication server
        $headers = @{
            "Content-Type" = "application/x-www-form-urlencoded"
        }
        $body = @{
            client_id = $clientID
            client_secret = $clientSecret
            grant_type = $grantType
        }

        # Invoke request to get the token
        try {
            $timestamp = [Int]((Get-Date).TimeOfDay.TotalMilliseconds)
            $response = Invoke-RestMethod -Uri $url -Headers $headers -Body $body -Method "POST"
        }
        catch {
            Write-Error "Error while invoking POST request. `r`n$($_.Exception.Message)"
            return $null
        }

        # Construct the headers for API calls
        $signatureBytes = [System.Text.Encoding]::UTF8.GetBytes("$($response.access_token):$($soin):$($timestamp)")
        $encodedSignature = [Convert]::ToBase64String($signatureBytes)
        $apiHeaders = @{
            "Content-Type" = "application/json"
            "Authorization" = "Bearer $($response.access_token)"
            "SOIN" = $soin
            "TimeStamp" = $timestamp
            "Signature" = $encodedSignature
            "Accept" = "application/json"
        }

        # Return the headers
        return $apiHeaders
    }
    catch {
        Write-Error "Exception occurred on line $($_.InvocationInfo.ScriptLineNumber) while retrieving Tech Data API headers: `r`n$($_.Exception.Message)"
        return $null
    }
}

<#
.SYNOPSIS
    This function establishes a connection with the Tech Data server using admin account credentials or a MSPComplete Endpoint.
.DESCRIPTION
    This function establishes a connection with the Tech Data server using admin account credentials or a MSPComplete Endpoint.
    It returns if the connection was successful, false otherwise.
.PARAMETER url
    The Tech Data authentication URL.
.PARAMETER clientID
    The Tech Data client ID.
.PARAMETER clientSecret
    The Tech Data client secret.
.PARAMETER grantType
    The Tech Data grant type.
.PARAMETER soin
    The Tech Data StreamOne Identification Number (SOIN)
.PARAMETER endpoint
    The MSPComplete Endpoint containing the Tech Data authentication URL as its "Url" property, the
    Tech Data client ID, client secret and grant type, and SOIN as
    "ClientID", "ClientSecret", "GrantType" and "SOIN" extended properties
.EXAMPLE
    Connect-TechDataAdminAccount -Endpoint $Endpoint
.EXAMPLE
    $Endpoint | Connect-TechDataAdminAccount
.EXAMPLE
    Connect-TechDataAdminAccount -Url $url -ClientID $clientID -ClientSecret $clientSecret -GrantType $grantType -Soin $soin
#>

function Connect-TechDataAdminAccount {
    param (
        # The Tech Data authentication URL.
        [Parameter(Mandatory=$true, ParameterSetName="credential")]
        [String]$url,

        # The Tech Data client ID.
        [Parameter(Mandatory=$true, ParameterSetName="credential")]
        [String]$clientID,

        # The Tech Data client secret.
        [Parameter(Mandatory=$true, ParameterSetName="credential")]
        [String]$clientSecret,

        # The Tech Data grant type.
        [Parameter(Mandatory=$true, ParameterSetName="credential")]
        [String]$grantType,

        # The Tech Data StreamOne Identification Number (SOIN)
        [Parameter(Mandatory=$true, ParameterSetName="credential")]
        [String]$soin,

        # The MSPComplete Endpoint containing the Tech Data authentication URL as its "Url" property, the
        # Tech Data client ID, client secret and grant type, and SOIN as
        # "ClientID", "ClientSecret", "GrantType" and "SOIN" extended properties
        [Parameter(Mandatory=$true, ParameterSetName="endpoint", ValueFromPipeline=$true)]
        $endpoint
    )

    try {
        # Retrieve Tech Data API headers
        Write-Information "Retrieving Tech Data API headers."
        if ($PSCmdlet.ParameterSetName -eq "credential") {
            # Create hash table for params
            $getTechDataApiHeadersParams = @{
                Url             = $url
                ClientID        = $clientID
                ClientSecret    = $clientSecret
                GrantType       = $grantType
                Soin            = $soin
            }
            $apiHeaders = Get-TechDataApiHeaders @getTechDataApiHeadersParams
        }
        else {
            $apiHeaders = Get-TechDataApiHeaders -Endpoint $endpoint
        }

        # Verify headers
        if (!$apiHeaders) {
            Write-Error "Failed to retrieve Tech Data API headers."
            return $false
        }

        # Set to global variable and return that connection was a success
        $Global:TechDataApiHeaders = $apiHeaders
        return $true
    }
    catch {
        Write-Error "Exception occurred on line $($_.InvocationInfo.ScriptLineNumber) while connecting to Tech Data: `r`n$($_.Exception.Message)"
        return $false
    }
}