Functions/Connect-SKYAPI.ps1

Function Connect-SKYAPI
{
    <#
        .LINK
        https://github.com/Sekers/SKYAPI/wiki
        
        .LINK
        Documentation: https://developer.blackbaud.com/skyapi/docs/authorization

        .SYNOPSIS
        Education Management School API - Verify cached tokens exist and are not expired using Connect-SKYAPI.
        Connect-SKYAPI will automatically refresh tokens or reauthenticate to the SKY API service, if necessary.

        .DESCRIPTION
        Education Management School API - Verify cached tokens exist and are not expired using Connect-SKYAPI.
        Connect-SKYAPI will automatically refresh tokens or reauthenticate to the SKY API service, if necessary.

        .PARAMETER ForceReauthentication
        Forces reauthentication.
        .PARAMETER ForceRefresh
        Forces token refresh.
        .PARAMETER ClearBrowserControlCache
        Used in conjunction with 'ForceReauthentication'. Clears the Microsoft Edge WebView2 or Internet Explorer control browser cache. Useful when troubleshooting authentication.
        .PARAMETER AuthenticationMethod
        Let's you specify how you want to authenticate if authentication is necessary:
        - EdgeWebView2 (default): Opens a web browser window using Microsoft Edge WebView2 for authentication.
                                    Requires the WebView2 Runtime to be installed. If not installed, will prompt for automatic installation.
        - LegacyIEControl: Opens a web browser window using the old Internet Explorer control. This is no longer supported by Blackbaud.
        - MiniHTTPServer: Alternate method of capturing the authentication using your user account's default web browser
                                    and listening for the authentication response using a temporary HTTP server hosted by the module.
        .PARAMETER ReturnConnectionInfo
        Returns connection information after performing function.

        .EXAMPLE
        Connect-SKYAPI
        .EXAMPLE
        Connect-SKYAPI -ForceReauthentication
        .EXAMPLE
        Connect-SKYAPI -ForceReauthentication -ClearBrowserControlCache
        .EXAMPLE
        Connect-SKYAPI -ForceReauthentication -AuthenticationMethod MiniHTTPServer
        .EXAMPLE
        Connect-SKYAPI -ForceRefresh
        .EXAMPLE
        Connect-SKYAPI -ReturnConnectionInfo
    #>


    [CmdletBinding(DefaultParameterSetName='NoParameters')]
    Param(
        [parameter(
        Position=0,
        ParameterSetName = 'ForceReauthentication',
        ValueFromPipeline=$true,
        ValueFromPipelineByPropertyName=$true)]
        [Switch]$ForceReauthentication,

        [parameter(
        Position=1,
        ParameterSetName = 'ForceRefresh',
        ValueFromPipeline=$true,
        ValueFromPipelineByPropertyName=$true)]
        [Switch]$ForceRefresh,

        [parameter(
        Position=2,
        Mandatory=$false,
        ValueFromPipeline=$true,
        ValueFromPipelineByPropertyName=$true)]
        [ValidateSet('EdgeWebView2','MiniHTTPServer','LegacyIEControl')]
        [string]$AuthenticationMethod,

        [parameter(
        Position=3,
        ValueFromPipeline=$true,
        ValueFromPipelineByPropertyName=$true)]
        [Switch]$ReturnConnectionInfo
    )

    DynamicParam
    {
        # Initialize Parameter Dictionary
        $ParameterDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
        
        # Make -ClearBrowserControlCache Parameter Only Appear if ForceReauthentication is Used
        # DynamicParameter1: ClearBrowserControlCache
        if ($ForceReauthentication)
        { 
            $ParameterAttributes = [System.Management.Automation.ParameterAttribute]@{
                ParameterSetName = "ForceReauthentication"
                Mandatory = $false
                ValueFromPipeline = $true
                ValueFromPipelineByPropertyName = $true
            }

            $AttributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
            $AttributeCollection.Add($ParameterAttributes)

            $DynamicParameter1 = [System.Management.Automation.RuntimeDefinedParameter]::new(
                'ClearBrowserControlCache', [switch], $AttributeCollection)

            $ParameterDictionary.Add('ClearBrowserControlCache', $DynamicParameter1)
        }

        return $ParameterDictionary
    }
    
    begin
    {
        $ClearBrowserControlCache = $PSBoundParameters['ClearBrowserControlCache']
    }

    process
    {
        # Set the Necesasary Configuration Variables
        $sky_api_config = Get-SKYAPIConfig -ConfigPath $sky_api_config_file_path
        $client_id = $sky_api_config.client_id
        $client_secret = $sky_api_config.client_secret
        $redirect_uri = $sky_api_config.redirect_uri
        $token_uri = $sky_api_config.token_uri

        # If Key File Does Not Exist or the -ForceReauthentication Parameter is Set, Ask User to Reauthenticate
        if ((-not (Test-Path $sky_api_tokens_file_path)) -or ($ForceReauthentication))
        {
            Get-SKYAPINewTokens -sky_api_tokens_file_path $sky_api_tokens_file_path -AuthenticationMethod $AuthenticationMethod -ClearBrowserControlCache:$ClearBrowserControlCache
        }

        # Get Tokens & Set Creation Times
        try
        {
            $AuthTokensFromFile = Get-SKYAPIAuthTokensFromFile
            $refresh_token_creation = $AuthTokensFromFile.refresh_token_creation
            $access_token_creation = $AuthTokensFromFile.access_token_creation    
        }
        catch
        {
            throw "Key JSON tokens file is corrupted or invalid. Please run Connect-SKYAPI with the -ForceReauthentication parameter to recreate it."    
        }

        # If Refresh Token Has Expired Because it Hasn't Been Used for Max Refresh Token Timespan, Ask User to Reauthenticate
        if (-not (Confirm-SKYAPITokenIsFresh -TokenCreation $refresh_token_creation -TokenType Refresh))
        {
            Get-SKYAPINewTokens -sky_api_tokens_file_path $sky_api_tokens_file_path -AuthenticationMethod $AuthenticationMethod -ClearBrowserControlCache:$ClearBrowserControlCache

            # Get Tokens & Set Creation Times
            try
            {
                $AuthTokensFromFile = Get-SKYAPIAuthTokensFromFile
                $refresh_token_creation = $AuthTokensFromFile.refresh_token_creation
                $access_token_creation = $AuthTokensFromFile.access_token_creation    
            }
            catch
            {
                throw "Key JSON tokens file is corrupted or invalid. Please run Connect-SKYAPI with the -ForceReauthentication parameter to recreate it."    
            }
        }

        # If the Access Token Expired OR the -ForceRefresh Parameter is Set, Then Refresh Access Token
        if ((-not (Confirm-SKYAPITokenIsFresh -TokenCreation $access_token_creation -TokenType Access)) -or ($ForceRefresh))
        {
            # Run Invoke Command and Catch Responses
            [int]$InvokeCount = 0
            [int]$MaxInvokeCount = 5
            do
            {      
                $InvokeCount += 1
                $NextAction = $null
                try
                {
                    # Swap Refresh token for an Access token (which when requested returns both refresh and access tokens)
                    $Authorization = Get-SKYAPIAccessToken -grant_type 'refresh_token' -client_id $client_id -redirect_uri $redirect_uri -client_secret $client_secret -authCode $($AuthTokensFromFile.refresh_token) -token_uri $token_uri
                }
                catch
                {
                    # Process Invoke Error
                    $LastCaughtError = ($_)
                    $NextAction = SKYAPICatchInvokeErrors($_)

                    # Just in case the token was refreshed by the error catcher, update the $AuthTokensFromFile variable
                    $AuthTokensFromFile = Get-SKYAPIAuthTokensFromFile
                }
            }while ($NextAction -eq 'retry' -and $InvokeCount -lt $MaxInvokeCount)

            if ($InvokeCount -ge $MaxInvokeCount)
            {
                Write-Warning $("Invoke tried running $InvokeCount times, but failed each time. " `
                + "It is possible that the Key JSON tokens file is corrupted or invalid. Try running Connect-SKYAPI with the -ForceReauthentication parameter to recreate it.")
                throw $LastCaughtError
            }
                
                # Save credentials to file
                $Authorization | ConvertTo-Json `
                    | ConvertTo-SecureString -AsPlainText -Force `
                    | ConvertFrom-SecureString `
                    | Out-File -FilePath $sky_api_tokens_file_path -Force
        }

        # Return the connection information, if requested.
        if ($ReturnConnectionInfo)
        {  
            # Collect the non-sensitive session information.
            # More info on these items here: https://developer.blackbaud.com/skyapi/docs/authorization/auth-code-flow/tutorial
            $ObjectPropertyNames = @(
                'environment_id'
                'environment_name'
                'legal_entity_id'
                'legal_entity_name'
                'user_id'
                'email'
                'family_name'
                'given_name'
                'mode'
                'refresh_token_creation'
                'access_token_creation'
            )
            Get-SKYAPIAuthTokensFromFile | Select-Object $ObjectPropertyNames
        }
    }
}