PsDos.OktaSDK.psm1

function Add-OktaErrorExceptionMessage {
    param(
        $webException,
        $url,
        $message
    )

    if($null -ne $webException.Response) {
        $stream = $webException.Response.GetResponseStream()
        $stream.Position = 0
        $reader = [System.IO.StreamReader]::new($Stream)
        $responseJson = $reader.ReadToEnd() | ConvertFrom-Json | ConvertTo-Json
    }

    $retVal = "$message `n URL: $url `n Web Request Error: $($webException.Message) `n Response Body: $responseJson"
    return $retVal
}

<#
.SYNOPSIS
Returns [{
            id = <appId>;
            name = <appName>;
            clientId = <client id>;
            secret = <client secret>;
        },
        ...]
OR
        {
            id = <appId>;
            name = <appName>;
            clientId = <client id>;
            secret = <client secret>;
        }
Throws exception if failed
 
.DESCRIPTION
Reaches out to the specified Okta instance in $baseUrl
and collects all applications according to the $appName
parameter. Returns a list of Okta Application objects.
See https://developer.okta.com/docs/reference/api/apps/#application-object
 
.PARAMETER appName
A name of the applications you want to retrieve. Okta will
do wildcard matching on this, so partial names are accepted.
 
.PARAMETER baseUrl
The base url of the okta instance you're trying to hit
 
.PARAMETER token$token
the Okta SSWS token used to authenticate with the Okta
API.
#>

function Get-OktaAppsByName {
    param(
        [string]$appName,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    $appsUrl = "$baseUrl/api/v1/apps?q=$appName"
    $headers = @{
        'Authorization' = "$tokenType $token";
        'Content-Type' = 'application/json';
        'Accept' = 'application/json';
    }

    try {
        $response = Invoke-RestMethod -Uri $appsUrl -Method 'GET' -Headers $headers
    } catch {
        $errMsg = "Error getting existing Okta Applications"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $appsUrl -message $errMsg
    }

    $retVal = foreach($app in $response) {
        $retVal + @{
            id = $app.id;
            name = $app.label;
            clientId = $app.credentials.oauthClient.client_id;
            secret = $app.credentials.oauthClient.client_secret;
        }
    }

    return $retVal
}

<#
.SYNOPSIS
Returns {
            id = <appId>;
            name = <appName>;
            clientId = <client id>;
            secret = <client secret>;
        }
Throws exception if failed
 
.DESCRIPTION
Reaches out to the specified Okta instance in $baseUrl
and retrieves one application according to the $appId
parameter. Returns a single application object taking the form
{
    appId = <id>;
    name = <name>;
    clientId = <client_id>;
    secret = <client_secret>;
}
The appId and client ID should always be the same
 
.PARAMETER appId
The Id of the application you want to retrieve.
 
.PARAMETER baseUrl
The base url of the okta instance you're trying to hit
 
.PARAMETER token$token
the Okta SSWS token used to authenticate with the Okta
API.
#>

function Get-OktaAppById {
    param(
        [string]$appId,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($appId)){
        throw "appId must not be empty"
    }
    
    $appsUrl = "$baseUrl/api/v1/apps/$appId"
    $headers = @{
        'Authorization' = "$tokenType $token";
        'Content-Type' = 'application/json';
        'Accept' = 'application/json';
    }

    try {
        $response = Invoke-RestMethod -Uri $appsUrl -Method 'GET' -Headers $headers
    } catch {
        $errMsg = "Error getting existing Okta Applications"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -message $errMsg
    }

    return @{
        id = $response.id;
        name = $response.label;
        clientId = $response.credentials.oauthClient.client_id;
        secret = $response.credentials.oauthClient.client_secret;
    }
}

function Add-OktaOIDCApplication
{
    param(
        $oktaAppRequestBody,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }
    
    $appsUrl = "$baseUrl/api/v1/apps"
    $headers = @{
        'Authorization' = "$tokenType $token";
        'Content-Type' = 'application/json';
        'Accept' = 'application/json';
    }

    $jsonBody = ConvertTo-Json -Depth 100 $oktaAppRequestBody

    try {
        $response = Invoke-RestMethod -Uri $appsUrl -Method 'POST' -Body $jsonBody -Headers $headers
    } catch {
        $errMsg = "Error creating an Okta Application"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $appsUrl -message $errMsg
    }
    $newApp = @{
        id = $response.id;
        name = $response.label;
        clientId = $response.credentials.oauthClient.client_id;
        secret = $response.credentials.oauthClient.client_secret;
    }

    # Find the ID of the Everyone group in Okta.
    $groupUrl = "$baseUrl/api/v1/groups?q=everyone"
    try {
        $everyoneGroup = Invoke-RestMethod -Uri $groupUrl -Method 'GET' -Headers $headers
    } catch {
        $errMsg = "Error getting the Everyone Group from Okta"
        Add-OktaErrorExceptionMessage -webException $_.Exception -url $groupUrl -message $errMsg
    }

    # Associate the new app to the Everyone group
    $appGroupUrl = "$appsUrl/$($response.id)/groups/$($everyoneGroup.id)"
    try {
        Invoke-RestMethod -Uri $appGroupUrl -Method 'PUT' -Headers $headers | Out-Null
    } catch {
        $errMsg = "Error adding ${newApp.name} to the Everyone group"
        Add-OktaErrorExceptionMessage -webException $_.Exception -url $appGroupUrl -message $errMsg
    }

    return $newApp
}

function Update-OktaOIDCApplication
{
    param(
        [string]$appId,
        $oktaAppRequestBody,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($appId)){
        throw "appId must not be empty"
    }

    $appsUrl = "$baseUrl/api/v1/apps/$appId"
    $headers = @{
        'Authorization' = "$tokenType $token";
        'Content-Type' = 'application/json';
        'Accept' = 'application/json';
    }

    $jsonBody = ConvertTo-Json -Depth 100 $oktaAppRequestBody
    try {
        $response = Invoke-RestMethod -Uri $appsUrl -Method 'PUT' -Body $jsonBody -Headers $headers
    } catch {
        $errMsg = "Error updating Okta application with id $appId"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $appsUrl -message $errMsg
    }

    $updatedApp = @{
        id = $response.id;
        name = $response.label;
        clientId = $response.credentials.oauthClient.client_id;
        secret = $response.credentials.oauthClient.client_secret;
    }

    # Find the ID of the Everyone group in Okta.
    $groupUrl = "$baseUrl/api/v1/groups?q=everyone"
    try {
        $everyoneGroup = Invoke-RestMethod -Uri $groupUrl -Method 'GET' -Headers $headers
    } catch {
        $errMsg = "Error getting the Everyone Group from Okta"
        Add-OktaErrorExceptionMessage -webException $_.Exception -url $groupUrl -message $errMsg
    }

    # Associate the new app to the Everyone group
    $appGroupUrl = "$appsUrl/groups/$($everyoneGroup.id)"
    try {
        Invoke-RestMethod -Uri $appGroupUrl -Method 'PUT' -Headers $headers | Out-Null
    } catch {
        $errMsg = "Error adding ${newApp.name} to the Everyone group"
        Add-OktaErrorExceptionMessage -webException $_.Exception -url $appGroupUrl -message $errMsg
    }

    return $updatedApp
}

function Get-WebApplicationBody {
    return @{
        name = 'oidc_client';
        label = '';
        signOnMode = 'OPENID_CONNECT';
        credentials = @{
            userNameTemplate = @{
                template = '${source.login}';
                type = 'BUILT_IN';
            };
            oauthClient = @{
                autoKeyRotation = $true;
                token_endpoint_auth_method = "client_secret_basic";
            }
        };
        settings = @{
            oauthClient = @{
                initiate_login_uri = '';
                client_uri = $null;
                logo_uri = $null;
                redirect_uris = '';
                post_logout_redirect_uris = '';
                response_types = @('code', 'token', 'id_token');
                grant_types = @('implicit','client_credentials','refresh_token','authorization_code');
                application_type = 'web';
                consent_method = 'TRUSTED';
                issuer_mode = 'ORG_URL';
            }
        }
    }
}

<#
.SYNOPSIS
Creates an Okta OIDC Application with type WEB.
Returns {
            id = <appId>;
            name = <appName>;
            clientId = <client id>;
            secret = <client secret>;
        }
throws exception if failed
 
.DESCRIPTION
Creates an OIDC application in okta that can use
client_credentials, code, and implicit flows. This is
usually reserved for serverside rendered web application
 
.PARAMETER appName
name of the application you want to create
 
.PARAMETER redirectUris
URIs that you want to redirect back to after
authenticating with Okta
 
.PARAMETER logoutUris
URIs that you want to navigate to upon logging
out of Okta
 
.PARAMETER baseUrl
the url of the Okta instance you want to hit
 
.PARAMETER token$token
the Okta SSWS token used to authenticate with the Okta
API
#>

function Add-OktaOIDCWebApp
{
    param(
        [string]$appName,
        [System.Array]$redirectUris,
        [System.Array]$logoutUris,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    Write-Host "***********Adding web application************"

    $body = Get-WebApplicationBody
    $body['label'] = $appName
    $body['settings']['oauthClient']['redirect_uris'] = $redirectUris
    $body['settings']['oauthClient']['post_logout_redirect_uris'] = $logoutUris

    return Add-OktaOIDCApplication `
        -oktaAppRequestBody $body `
        -baseUrl $baseUrl `
        -token $token `
        -tokenType $tokenType
}

<#
.SYNOPSIS
Updates an Okta OIDC Application with type WEB.
Returns {
            id = <appId>;
            name = <appName>;
            clientId = <client id>;
            secret = <client secret>;
        }
Returns $null if failed
 
.DESCRIPTION
Updates an Okta OIDC Application with type WEB
 
.PARAMETER appId
id of the application you wish to update
 
.PARAMETER appName
new name of the application
 
.PARAMETER redirectUris
new redirect URIs for the application
 
.PARAMETER logoutUris
new logout redirect URIs for the application
 
.PARAMETER baseUrl
the url of the Okta instance you want to hit
 
.PARAMETER token$token
the Okta SSWS token used to authenticate with the Okta
API
#>

function Update-OktaOIDCWebApp
{
    param(
        [string]$appId,
        [string]$appName,
        [System.Array]$redirectUris,
        [System.Array]$logoutUris,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($appId)){
        throw "appId must not be empty"
    }

    Write-Host "***********Updating web application************"

    $body = Get-WebApplicationBody
    $body['label'] = $appName
    $body['settings']['oauthClient']['redirect_uris'] = $redirectUris
    $body['settings']['oauthClient']['post_logout_redirect_uris'] = $logoutUris
    $body['credentials']['oauthClient']['client_id'] = $appId

    return Update-OktaOIDCApplication `
        -appId $appId `
        -oktaAppRequestBody $body `
        -baseUrl $baseUrl `
        -token $token `
        -tokenType $tokenType
}

function Get-SpaApplicationBody {
    @{
        name = 'oidc_client';
        label = '';
        signOnMode = 'OPENID_CONNECT';
        credentials = @{
            userNameTemplate = @{
                template = '${source.login}';
                type = 'BUILT_IN';
            };
            oauthClient = @{
                autoKeyRotation = $true;
                token_endpoint_auth_method = "none";
            }
        };
        settings = @{
            oauthClient = @{
                client_uri = $null;
                logo_uri = $null;
                redirect_uris = '';
                post_logout_redirect_uris = '';
                response_types = @('code');
                grant_types = @('authorization_code');
                initiate_login_uri = '';
                application_type = 'browser';
                consent_method = 'TRUSTED';
                issuer_mode = 'ORG_URL';
            }
        }
    }
}

<#
.SYNOPSIS
Returns {
            id = <appId>;
            name = <appName>;
            clientId = <client id>;
            secret = <client secret>;
        }
Returns $null if failed
 
.DESCRIPTION
Creates an OIDC application in okta that can use
code+PKCE, and implicit flows. This should be used
for Single Page Applications
 
.PARAMETER appName
name of the application you want to create
 
.PARAMETER redirectUris
URIs that you want to redirect back to after
authenticating with Okta
 
.PARAMETER logoutUris
URIs that you want to navigate to upon logging
out of Okta
 
.PARAMETER baseUrl
the url of the Okta instance you want to hit
 
.PARAMETER token$token
the Okta SSWS token used to authenticate with the Okta
API
#>

function Add-OktaOIDCSinglePageApp
{
    param(
        [string]$appName,
        [System.Array]$redirectUris,
        [System.Array]$logoutUris,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    Write-Host "***********Adding client Single Page application************"

    $body = Get-SpaApplicationBody
    $body['label'] = $appName
    $body['settings']['oauthClient']['redirect_uris'] = $redirectUris
    $body['settings']['oauthClient']['post_logout_redirect_uris'] = $logoutUris

    return Add-OktaOIDCApplication `
        -oktaAppRequestBody $body `
        -baseUrl $baseUrl `
        -token $token `
        -tokenType $tokenType
}

<#
.SYNOPSIS
Updates an Okta OIDC Application with type SPA.
Returns {
            id = <appId>;
            name = <appName>;
            clientId = <client id>;
            secret = <client secret>;
        }
Returns $null if failed
 
.DESCRIPTION
Updates an Okta OIDC Application with type SPA
 
.PARAMETER appId
id of the SPA application you wish to update
 
.PARAMETER appName
new name of the application
 
.PARAMETER redirectUris
new redirect URIs for the application
 
.PARAMETER logoutUris
new logout redirect URIs for the application
 
.PARAMETER baseUrl
the url of the Okta instance you want to hit
 
.PARAMETER token$token
the Okta SSWS token used to authenticate with the Okta
API
#>

function Update-OktaOIDCSinglePageApp
{
    param(
        [string]$appId,
        [string]$appName,
        [System.Array]$redirectUris,
        [System.Array]$logoutUris,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($appId)){
        throw "appId must not be empty"
    }

    Write-Host "***********Updating client Single Page application************"

    $body = Get-SpaApplicationBody
    $body['label'] = $appName
    $body['settings']['oauthClient']['redirect_uris'] = $redirectUris
    $body['settings']['oauthClient']['post_logout_redirect_uris'] = $logoutUris
    $body['credentials']['oauthClient']['client_id'] = $appId

    return Update-OktaOIDCApplication `
        -appId $appId `
        -oktaAppRequestBody $body `
        -baseUrl $baseUrl `
        -token $token `
        -tokenType $tokenType
}

function Get-ClientCredentialsApplicationBody {
    return @{
        name = 'oidc_client';
        label = '';
        signOnMode = 'OPENID_CONNECT';
        credentials = @{
            userNameTemplate = @{
                template = '${source.login}';
                type = 'BUILT_IN';
            };
            oauthClient = @{
                autoKeyRotation = $true;
                token_endpoint_auth_method = "client_secret_basic";
            }
        };
        settings = @{
            oauthClient = @{
                client_uri = $null;
                logo_uri = $null;
                response_types = @('token');
                grant_types = @('client_credentials');
                application_type = 'service';
                issuer_mode = 'ORG_URL';
            }
        }
    }
}

<#
.SYNOPSIS
Creates an Okta OIDC Application with type Service.
Returns {
            id = <appId>;
            name = <appName>;
            clientId = <client id>;
            secret = <client secret>;
        }
Returns $null if failed
 
.DESCRIPTION
Creates an OIDC application in okta that can use
client credentials flow. This should be used
for Machine To Machine applications that don't
have a user context
 
.PARAMETER appName
name of the application you want to create
 
.PARAMETER baseUrl
the url of the Okta instance you want to hit
 
.PARAMETER token$token
the Okta SSWS token used to authenticate with the Okta
API
#>

function Add-OktaClientCredentialsApp
{
    param(
        [string]$appName,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    Write-Host "***********Adding client credentials application************"

    $body = Get-ClientCredentialsApplicationBody
    $body['label'] = $appName

    return Add-OktaOIDCApplication `
        -oktaAppRequestBody $body `
        -baseUrl $baseUrl `
        -token $token `
        -tokenType $tokenType
}

<#
.SYNOPSIS
Updates an Okta OIDC Application with type Service.
Returns {
            id = <appId>;
            name = <appName>;
            clientId = <client id>;
            secret = <client secret>;
        }
Returns $null if failed
 
.DESCRIPTION
Updates an Okta OIDC Application with type Service
 
.PARAMETER appId
id of the SPA application you wish to update
 
.PARAMETER appName
new name of the application
 
.PARAMETER baseUrl
the url of the Okta instance you want to hit
 
.PARAMETER token$token
the Okta SSWS token used to authenticate with the Okta
API
#>

function Update-OktaClientCredentialsApp
{
    param(
        [string]$appId,
        [string]$appName,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($appId)){
        throw "appId must not be empty"
    }

    Write-Host "***********Updating client credentials application************"

    $body = Get-ClientCredentialsApplicationBody
    $body['label'] = $appName
    $body['credentials']['oauthClient']['client_id'] = $appId

    return Update-OktaOIDCApplication `
        -appId $appId `
        -oktaAppRequestBody $body `
        -baseUrl $baseUrl `
        -token $token `
        -tokenType $tokenType
}

<#
.SYNOPSIS
Deletes and Okta application by ID.
Returns $true if passed
Returns $false if failed
 
.DESCRIPTION
Deletes and Okta application by ID
 
.PARAMETER appId
Id of the application you want to delete.
Can be the Raw ID or Client Id
 
.PARAMETER baseUrl
The base url of the okta instance you're trying to hit.
 
.PARAMETER token$token
the Okta SSWS Token used to authenticate with the API
#>

function Remove-OktaApp {
    param(
        [string]$appId,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($appId)){
        throw "appId must not be empty"
    }
    
    $appsUrl = "$baseUrl/api/v1/apps/$appId"
    $headers = @{
        'Authorization' = "$tokenType $token";
        'Content-Type' = 'application/json';
        'Accept' = 'application/json';
    }

    try {
        Invoke-RestMethod -Uri $appsUrl -Method 'DELETE' -Headers $headers
    } catch {
        $errMsg = "Error deleting Okta Application with Id $appId"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $appsUrl -message $errMsg
    }

    Write-Host "Successfully deleted Okta application"
    return $true
}

<#
.SYNOPSIS
Returns [{
            id = <id>;
            name = <name>;
            audiences = [];
            issuer = <issuer>;
            status = <status>;
        },
        ...]
OR
        {
            id = <id>;
            name = <name>;
            audiences = [];
            issuer = <issuer>;
            status = <status>;
        }
Throws exception if failed
 
.DESCRIPTION
Retrieves a list of Okta Authorization Servers by their names.
This does wildcard matching, so partial names are accepted.
 
.PARAMETER authServerName
Name of the auth servers you want to find.
 
.PARAMETER baseUrl
base url of the Okta instance you want to hit
 
.PARAMETER token$token
Okta SSWS token that you would use to authenticate with the Okta
API
#>

function Get-OktaAuthorizationServersByName 
{
    param(
        [string]$authServerName,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($authServerName)){
        throw "authServerName must not be empty"
    }

    $baseUrl = "$baseUrl/api/v1/authorizationServers?q=$authServerName"

    $headers = @{
        'Accept' = 'application/json'
        'Content-Type' = 'application/json'
        'Authorization' = "$tokenType $token"
    }

    try {
        $authServers = Invoke-RestMethod -Uri $baseUrl -Method 'GET' -Headers $headers
    } catch {
        $errMsg = "Error finding the authorization server $authServerName"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $baseUrl -message $errMsg
    }

    $retVal = foreach($authServer in $authServers) {
        $retVal + @{
            id = $authServer.id;
            name = $authServer.name;
            audiences = $authServer.audiences;
            issuer = $authServer.issuer;
            status = $authServer.status;
        }
    }

    return $retVal
}

<#
.SYNOPSIS
Returns {
            id = <id>;
            name = <name>;
            audiences = [];
            issuer = <issuer>;
            status = <status>;
        }
Throws exception if failed
 
.DESCRIPTION
Retrieves a single Okta Authorization Server by it's ID taking
the form
{
    id = <id>;
    name = <name>;
    audiences = <audiences>;
    issuer = <issuer>;
    status = <status>;
}
 
.PARAMETER authServerId
ID of the auth servers you want to find.
 
.PARAMETER baseUrl
base url of the Okta instance you want to hit
 
.PARAMETER token$token
Okta SSWS token that you would use to authenticate with the Okta
API
#>

function Get-OktaAuthorizationServerById
{
    param(
        [string]$authServerId,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($authServerId)){
        throw "authServerId must not be empty"
    }

    $baseUrl = "$baseUrl/api/v1/authorizationServers/$authServerId"
    $headers = @{
        'Accept' = 'application/json'
        'Content-Type' = 'application/json'
        'Authorization' = "$tokenType $token"
    }

    try {
        $authServer = Invoke-RestMethod -Uri $baseUrl -Method 'GET' -Headers $headers
    } catch {
        $errMsg = "Error getting auth server with id $authServerId"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $baseUrl -message $errMsg
    }

    return @{
        id = $authServer.id;
        name = $authServer.name;
        audiences = $authServer.audiences;
        issuer = $authServer.issuer;
        status = $authServer.status;
    }
}

<#
.SYNOPSIS
Returns [{
            id = <scope id>;
            name = <scope name>;
        },
        ...]
OR
        {
            id = <scope id>;
            name = <scope name>;
        }
Throws exception on error
 
.DESCRIPTION
Retrieves a list of Okta Authorization Server Scopes by their names.
This does wildcard matching, so partial names are accepted.
 
.PARAMETER authServerId
Id of the auth server you're trying to hit
 
.PARAMETER scopeName
Name of the auth server scopes you want to find.
 
.PARAMETER baseUrl
base url of the Okta instance you want to hit
 
.PARAMETER token$token
Okta SSWS token that you would use to authenticate with the Okta
API
#>

function Get-OktaAuthorizationServerScopesByName {
    param(
        [string]$authServerId,
        [string]$scopeName,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($authServerId)){
        throw "authServerId must not be empty"
    }

    if([string]::IsNullOrEmpty($scopeName)){
        throw "scopeName must not be empty"
    }

    $baseUrl = "$baseUrl/api/v1/authorizationServers/$authServerId/scopes?q=$scopeName"
    $headers = @{
        'Accept' = 'application/json'
        'Content-Type' = 'application/json'
        'Authorization' = "$tokenType $token"
    }

    try {
        $scopes = Invoke-RestMethod -Uri $baseUrl -Method 'GET' -Headers $headers
    } catch {
        $errMsg = "Error getting auth server scopes with name $scopeName"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $baseUrl -message $errMsg
    }

    $retVal = foreach($scope in $scopes) {
        @{
            id = $scope.id;
            name = $scope.name;
        }
    }

    return $retVal
}

<#
.SYNOPSIS
Returns {
            id = <scope id>;
            name = <scope name>;
        }
Throws exception on error
 
.DESCRIPTION
Retrieves a single Okta Authorization Server Scope by it's ID.
 
.PARAMETER authServerId
Id of the auth server you're trying to hit
 
.PARAMETER scopeId
Id of the auth server scope you want to find.
 
.PARAMETER baseUrl
base url of the Okta instance you want to hit
 
.PARAMETER token$token
Okta SSWS token that you would use to authenticate with the Okta
API
#>

function Get-OktaAuthorizationServerScopesById {
    param(
        [string]$authServerId,
        [string]$scopeId,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($authServerId)){
        throw "authServerId must not be empty"
    }

    if([string]::IsNullOrEmpty($scopeId)){
        throw "scopeId must not be empty"
    }

    $baseUrl = "$baseUrl/api/v1/authorizationServers/$authServerId/scopes/$scopeId"
    $headers = @{
        'Accept' = 'application/json'
        'Content-Type' = 'application/json'
        'Authorization' = "$tokenType $token"
    }

    try {
        $scope = Invoke-RestMethod -Uri $baseUrl -Method 'GET' -Headers $headers
    } catch {
        $errMsg = "Error getting auth server scopes with id $scopeId"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $baseUrl -message $errMsg
    }

    return @{
        id = $scope.id;
        name = $scope.name;
    }
}

<#
.SYNOPSIS
Returns {
            id = <scope id>;
            name = <scope name>;
        }
Throws exception on error
 
.DESCRIPTION
Creates a single Okta Authorization Server Scope
 
.PARAMETER authServerId
Id of the auth server you want to add the scope to
 
.PARAMETER scopeName
Name of the scope
 
.PARAMETER description
Description of what the scope is for
 
.PARAMETER baseUrl
Base url of the okta instance you're trying to hit
 
.PARAMETER token$token
Okta SSWS token used to authenticate with the Okta API
#>

function Add-OktaAuthorizationServerScope 
{
    param(
        [string]$authServerId,
        [string]$scopeName,
        [string]$description,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($authServerId)){
        throw "authServerId must not be empty"
    }

    $scopeUrl = "$baseUrl/api/v1/authorizationServers/$authServerId/scopes"
    $headers = @{
        'Accept' = 'application/json'
        'Content-Type' = 'application/json'
        'Authorization' = "$tokenType $token"
    }

    $body = @{
        name = $scopeName;
        description = $description;
        system = $false;
        default = $false;
        displayName = $scopeName;
        consent = 'IMPLICIT';
        metadataPublish = 'ALL_CLIENTS';
    }

    $jsonBody = ConvertTo-Json $body

    try {
        $response = Invoke-RestMethod -Uri $scopeUrl -Method 'POST' -Body $jsonBody -Headers $headers
    } catch {
        $errMsg = "Error creating scope on authorization server with id $authServerId"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $scopeUrl -message $errMsg
    }
    
    return @{
        id = $response.id;
        name = $response.name;
    }
}

<#
.SYNOPSIS
Returns {
            id = <scope id>;
            name = <scope name>;
        }
Throws exception on error
 
.DESCRIPTION
Updates a single Okta Authorization Server Scope
 
.PARAMETER authServerId
Id of the auth server where the scope resides
 
.PARAMETER scopeId
Id of the scope you wish to change
 
.PARAMETER scopeName
New name of the scope
 
.PARAMETER description
New description of what the scope is for
 
.PARAMETER baseUrl
Base url of the okta instance you're trying to hit
 
.PARAMETER token$token
Okta SSWS token used to authenticate with the Okta API
#>

function Update-OktaAuthorizationServerScope 
{
    param(
        [string]$authServerId,
        [string]$scopeId,
        [string]$scopeName,
        [string]$description,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($authServerId)){
        throw "authServerId must not be empty"
    }

    if([string]::IsNullOrEmpty($scopeId)){
        throw "scopeId must not be empty"
    }

    $scopeUrl = "$baseUrl/api/v1/authorizationServers/$authServerId/scopes/$scopeId"
    $headers = @{
        'Accept' = 'application/json'
        'Content-Type' = 'application/json'
        'Authorization' = "$tokenType $token"
    }

    $body = @{
        name = $scopeName;
        description = $description;
        system = $false;
        default = $false;
        displayName = $scopeName;
        consent = 'IMPLICIT';
        metadataPublish = 'ALL_CLIENTS';
    }

    $jsonBody = ConvertTo-Json $body
    try {
        $response = Invoke-RestMethod -Uri $scopeUrl -Method 'PUT' -Body $jsonBody -Headers $headers
    } catch {
        $errMsg = "Error updating scope on authorization server with id $authServerId"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $scopeUrl -message $errMsg
    }

    return @{
        id = $response.id;
        name = $response.name;
    }
}

<#
.SYNOPSIS
Returns $true if passed
Returns $false if failed
 
.DESCRIPTION
Removes a single scope from an Okta Authorization Server
 
.PARAMETER authServerId
Id of the authorization server you wish to remove the scope from
 
.PARAMETER scopeId
Id of the scope you wish to remove
 
.PARAMETER baseUrl
Base url of the Okta instance you wish to hit
 
.PARAMETER token$token
Okta SSWS token used to authenticate with the Okta API
#>

function Remove-OktaAuthorizationServerScope {
    param(
        [string]$authServerId,
        [string]$scopeId,
        [string]$baseUrl,
        [string]$token,
        [string]$tokenType
    )
    
    if($tokenType -ne 'SSWS' -and $tokenType -ne 'Bearer') {
        throw 'tokenType must be either SSWS or Bearer'
    }

    if([string]::IsNullOrEmpty($authServerId)){
        throw "authServerId must not be empty"
    }

    if([string]::IsNullOrEmpty($scopeId)){
        throw "scopeId must not be empty"
    }

    $scopeUrl = "$baseUrl/api/v1/authorizationServers/$authServerId/scopes/$scopeId"
    $headers = @{
        'Accept' = 'application/json'
        'Content-Type' = 'application/json'
        'Authorization' = "$tokenType $token"
    }

    try {
        Invoke-RestMethod -Uri $scopeUrl -Method 'DELETE' -Headers $headers
    } catch {
        $errMsg = "Error deleting scope on authorization server with id $authServerId"
        throw Add-OktaErrorExceptionMessage -webException $_.Exception -url $scopeUrl -message $errMsg
    }

    Write-Host "Successfully deleted Okta scope on auth server with id $authServerId"
}

Export-ModuleMember -Function Get-OktaAppsByName
Export-ModuleMember -Function Get-OktaAppById
Export-ModuleMember -Function Add-OktaClientCredentialsApp
Export-ModuleMember -Function Add-OktaOIDCSinglePageApp
Export-ModuleMember -Function Add-OktaOIDCWebApp
Export-ModuleMember -Function Update-OktaClientCredentialsApp
Export-ModuleMember -Function Update-OktaOIDCSinglePageApp
Export-ModuleMember -Function Update-OktaOIDCWebApp
Export-ModuleMember -Function Update-OktaAuthorizationServerScope
Export-ModuleMember -Function Add-OktaAuthorizationServerScope
Export-ModuleMember -Function Get-OktaAuthorizationServerById
Export-ModuleMember -Function Get-OktaAuthorizationServersByName 
Export-ModuleMember -Function Get-OktaAuthorizationServerScopesByName
Export-ModuleMember -Function Get-OktaAuthorizationServerScopesById
Export-ModuleMember -Function Remove-OktaAuthorizationServerScope
Export-ModuleMember -Function Get-OktaAccessToken

# SIG # Begin signature block
# MIIcSgYJKoZIhvcNAQcCoIIcOzCCHDcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDNikSXy7NIbWRy
# LJAn9z5xVF4ia46N69PqOK28+ju5/qCCCqMwggUwMIIEGKADAgECAhAECRgbX9W7
# ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBa
# Fw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lD
# ZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3
# DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/l
# qJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fT
# eyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqH
# CN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+
# bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLo
# LFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIB
# yTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK
# BggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
# Y3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHow
# eDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwA
# AgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAK
# BghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0j
# BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7s
# DVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGS
# dQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6
# r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo
# +MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qz
# sIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHq
# aGxEMrJmoecYpJpkUe8wggVrMIIEU6ADAgECAhAMMCpTLsjxo9FR9hag8ePUMA0G
# CSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0
# IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwHhcNMjAwMzMxMDAwMDAw
# WhcNMjMwNTEwMTIwMDAwWjCBpzELMAkGA1UEBhMCVVMxDTALBgNVBAgTBFV0YWgx
# FzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVIZWFsdGggQ2F0YWx5
# c3QsIEluYy4xHjAcBgNVBAMTFUhlYWx0aCBDYXRhbHlzdCwgSW5jLjEwMC4GCSqG
# SIb3DQEJARYhYWRtaW5uaXN0cmF0b3JAaGVhbHRoY2F0YWx5c3QuY29tMIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2fY0HWdxDJezDOsbHp7f9u/lrrD5
# nuZ1mENMgvixlrtC/KXgBRXlcWH7ajIOKljKnWCSAZwlZy4nFGbMagKmMzohXUXg
# xo94u5nCdiBa/kgPazNGpL0AyGgX2VARMbcpm8Gdy+/uH3Kc7L91lcoGZVVBnVIt
# 1oj5iXURqmhL83TrMyYqyj3XOH0So8Y10FVLPSukocMzMqBIRgvn/7EP0iWtOjXx
# +o1wB5Ql+z9G3NCqF6CKE/Pn355XYbbmjF7BPzKoOjocHO6VU2uEflJWq1ZFb0QY
# /tAosyyLYi9kFfO1damtJfRbbsVqavwg2UeQkzhg9CpB6eSsmBXPlFHudQIDAQAB
# o4IBxTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0O
# BBYEFFjfHOOIre2C4m9NCk8TFJlDwMxUMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUE
# DDAKBggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2Ny
# bDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUw
# QzA3BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNl
# cnQuY29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNp
# Z25pbmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAsBxn
# 9yJAQi+9cJPZpJvOEV6iHaOBGv8898wNJCc4eB5g8WPziEY70GZVeqEdx3z0wS8U
# QQIr19Hkju2NFZjDtzB9z1jAc/9EgqFGoCZbPijv1EYAa2oOVAp1BPbLjqBSdXqu
# 2mzqo14CJ30oNom9ep9F6LGZ5zEoPsMrJejSbJGr4EacrksX8C8qeFklc7FzwiGk
# GX7IQxidrrhOm2fOvGGAAxnvNYAR0FqJK0LiWWPSt5R/j63H/6HQtqD2sLevI3+O
# bRP74TPchDobFmWlSogX9oB63E7fsbDAqecY0cRPQ6tVWK53Ke2sB514nahFjZDa
# mxsa3/acZWL659ly3jGCEP0wghD5AgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYD
# VQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAv
# BgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EC
# EAwwKlMuyPGj0VH2FqDx49QwDQYJYIZIAWUDBAIBBQCgfDAQBgorBgEEAYI3AgEM
# MQIwADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgkLTDOrnVHkrHcgYOgp3WH30N
# bWe5Y07xgpySlMemWcQwDQYJKoZIhvcNAQEBBQAEggEATGMQ+69SSc3K7avLGh9h
# HpLpjVhW15MqsRMwbcDXa6kk+z0EbJIugGc5gpig0Rr1GT8lGvyQZEn9tb7O43pC
# +bYHliMxKcbt6/5CrmJwL921gn5tCKNoRXBnXe0Hu7tTVDL8+QmMnwRWqllNNv96
# WJx0otxfcVlVwbbftA+pg6tZQLWjLkGyDJeq9mQ9ZQB6TpanPCE97DW77Rl1m8Mb
# Q3wdD860apywC0UqXqiZxTIuTLOxEa+tehuh5OVg1ZEiMy/rTSz/FmJGclBXwAER
# ERSq6iXsd8Qxn+ycF9+LQ9HbrFhUZIkJmidUxVYLCiu8wSsKP6SIoCCjNZf0agKs
# PaGCDskwgg7FBgorBgEEAYI3AwMBMYIOtTCCDrEGCSqGSIb3DQEHAqCCDqIwgg6e
# AgEDMQ8wDQYJYIZIAWUDBAIBBQAweAYLKoZIhvcNAQkQAQSgaQRnMGUCAQEGCWCG
# SAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCBwIkpCgXMsdps5Uik/czQBMucTB+3p
# ShbbcRBDHM0bdQIRAOxXFa1bI0j2fi8oYWcNWvwYDzIwMjAxMjE1MjEzODQ2WqCC
# C7swggaCMIIFaqADAgECAhAEzT+FaK52xhuw/nFgzKdtMA0GCSqGSIb3DQEBCwUA
# MHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT
# EHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJl
# ZCBJRCBUaW1lc3RhbXBpbmcgQ0EwHhcNMTkxMDAxMDAwMDAwWhcNMzAxMDE3MDAw
# MDAwWjBMMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJDAi
# BgNVBAMTG1RJTUVTVEFNUC1TSEEyNTYtMjAxOS0xMC0xNTCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBAOlkNZz6qZhlZBvkF9y4KTbMZwlYhU0w4Mn/5Ts8
# EShQrwcx4l0JGML2iYxpCAQj4HctnRXluOihao7/1K7Sehbv+EG1HTl1wc8vp6xF
# fpRtrAMBmTxiPn56/UWXMbT6t9lCPqdVm99aT1gCqDJpIhO+i4Itxpira5u0yfJl
# EQx0DbLwCJZ0xOiySKKhFKX4+uGJcEQ7je/7pPTDub0ULOsMKCclgKsQSxYSYAtp
# IoxOzcbVsmVZIeB8LBKNcA6Pisrg09ezOXdQ0EIsLnrOnGd6OHdUQP9PlQQg1OvI
# zocUCP4dgN3Q5yt46r8fcMbuQhZTNkWbUxlJYp16ApuVFKMCAwEAAaOCAzgwggM0
# MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG
# AQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsG
# AQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUH
# AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy
# AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj
# AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg
# AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ
# AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt
# AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj
# AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl
# AHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTAfBgNVHSMEGDAWgBT0tuEgHf4prtLk
# YaWyoiWyyBc1bjAdBgNVHQ4EFgQUVlMPwcYHp03X2G5XcoBQTOTsnsEwcQYDVR0f
# BGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJl
# ZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFz
# c3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGGGGh0dHA6
# Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2NhY2VydHMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVzdGFtcGluZ0NB
# LmNydDANBgkqhkiG9w0BAQsFAAOCAQEALoOhRAVKBOO5MlL62YHwGrv4CY0juT3Y
# kqHmRhxKL256PGNuNxejGr9YI7JDnJSDTjkJsCzox+HizO3LeWvO3iMBR+2VVIHg
# gHsSsa8Chqk6c2r++J/BjdEhjOQpgsOKC2AAAp0fR8SftApoU39aEKb4Iub4U5Ix
# X9iCgy1tE0Kug8EQTqQk9Eec3g8icndcf0/pOZgrV5JE1+9uk9lDxwQzY1E3Vp5H
# BBHDo1hUIdjijlbXST9X/AqfI1579JSN3Z0au996KqbSRaZVDI/2TIryls+JRtwx
# spGQo18zMGBV9fxrMKyh7eRHTjOeZ2ootU3C7VuXgvjLqQhsUwm09zCCBTEwggQZ
# oAMCAQICEAqhJdbWMht+QeQF2jaXwhUwDQYJKoZIhvcNAQELBQAwZTELMAkGA1UE
# BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj
# ZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4X
# DTE2MDEwNzEyMDAwMFoXDTMxMDEwNzEyMDAwMFowcjELMAkGA1UEBhMCVVMxFTAT
# BgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEx
# MC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBD
# QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3QMu5LzY9/3am6gpnF
# OVQoV7YjSsQOB0UzURB90Pl9TWh+57ag9I2ziOSXv2MhkJi/E7xX08PhfgjWahQA
# OPcuHjvuzKb2Mln+X2U/4Jvr40ZHBhpVfgsnfsCi9aDg3iI/Dv9+lfvzo7oiPhis
# EeTwmQNtO4V8CdPuXciaC1TjqAlxa+DPIhAPdc9xck4Krd9AOly3UeGheRTGTSQj
# MF287DxgaqwvB8z98OpH2YhQXv1mblZhJymJhFHmgudGUP2UKiyn5HU+upgPhH+f
# MRTWrdXyZMt7HgXQhBlyF/EXBu89zdZN7wZC/aJTKk+FHcQdPK/P2qwQ9d2srOlW
# /5MCAwEAAaOCAc4wggHKMB0GA1UdDgQWBBT0tuEgHf4prtLkYaWyoiWyyBc1bjAf
# BgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzASBgNVHRMBAf8ECDAGAQH/
# AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB5BggrBgEF
# BQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBD
# BggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2Ny
# bDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDig
# NoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
# dENBLmNybDBQBgNVHSAESTBHMDgGCmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYc
# aHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzALBglghkgBhv1sBwEwDQYJKoZI
# hvcNAQELBQADggEBAHGVEulRh1Zpze/d2nyqY3qzeM8GN0CE70uEv8rPAwL9xafD
# DiBCLK938ysfDCFaKrcFNB1qrpn4J6JmvwmqYN92pDqTD/iy0dh8GWLoXoIlHsS6
# HHssIeLWWywUNUMEaLLbdQLgcseY1jxk5R9IEBhfiThhTWJGJIdjjJFSLK8pieV4
# H9YLFKWA1xJHcLN11ZOFk362kmf7U2GJqPVrlsD0WGkNfMgBsbkodbeZY4UijGHK
# eZR+WfyMD+NvtQEmtmyl7odRIeRYYJu6DC0rbaLEfrvEJStHAgh8Sa4TtuF8QkIo
# xhhWz0E0tmZdtnR79VYzIi8iNrJLokqV2PWmjlIxggJNMIICSQIBATCBhjByMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg
# VGltZXN0YW1waW5nIENBAhAEzT+FaK52xhuw/nFgzKdtMA0GCWCGSAFlAwQCAQUA
# oIGYMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcN
# MjAxMjE1MjEzODQ2WjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBQDJb1QXtqWMC3C
# L0+gHkwovig0xTAvBgkqhkiG9w0BCQQxIgQgxaAHomr0bFEjql6UA5KCzNtZbUz/
# 9jcZkZHmsbayf0cwDQYJKoZIhvcNAQEBBQAEggEAQ0+EYOb0ocbsBTlmp0umv+HZ
# f+/94PXQ+sIrChVi+joU1OCQls5QushmnWyDvFKX4Hy2ok25ysBS48ugWc59d+Pv
# hWp3ZvSbd3pfZWazuNToqvq6dyIwJjqP4VR3ms22DPD4gD1RbU1D0/+rWTkcrgNY
# lBTuOSjjyd0cRV38VM5ijTV3szHGRa5afnEOiozxPbfOoVpkYaaLg4ZUG9bGJW0o
# 2JGFQm++95VOWphgYeDIvn0Ioj2nR4fX7rM2jZx+38orzquMqiUsG62+ZG2n/bjh
# avnQgvKEQlycO9A2D8u8IACYW4BbPNNxjzPMLNe/QwS+gIDYJdwy/uOOx3LzxQ==
# SIG # End signature block