Public/Authentication/Connect-FloRecruit.ps1

function Connect-FloRecruit {
    <#
    .SYNOPSIS
        Authenticates to the FloRecruit API and creates a session.

    .DESCRIPTION
        Authenticates using email and password (with optional MFA) to the FloRecruit API.
        Creates a session object that is used for all subsequent API calls.
        The session includes the authentication token and credentials for automatic re-authentication.

    .PARAMETER OrganizationName
        The FloRecruit organization name (used in the URL).

    .PARAMETER Email
        The email address for authentication.

    .PARAMETER Password
        The password as a SecureString (recommended).

    .PARAMETER PasswordPlainText
        The password as plain text (use only when necessary).

    .PARAMETER MFASecret
        The MFA secret as a SecureString (if MFA is enabled).

    .PARAMETER BaseUrl
        Override the base API URL (for testing/staging environments).

    .EXAMPLE
        $password = Read-Host -AsSecureString -Prompt "Enter Password"
        Connect-FloRecruit -OrganizationName "myorg" -Email "admin@myorg.com" -Password $password

    .EXAMPLE
        $password = Read-Host -AsSecureString -Prompt "Enter Password"
        $mfa = Read-Host -AsSecureString -Prompt "Enter MFA Secret"
        Connect-FloRecruit -OrganizationName "myorg" -Email "admin@myorg.com" -Password $password -MFASecret $mfa
    #>

    [CmdletBinding(DefaultParameterSetName = 'SecurePassword')]
    param(
        [Parameter(Mandatory)]
        [string]$OrganizationName,

        [Parameter(Mandatory)]
        [string]$Email,

        [Parameter(Mandatory, ParameterSetName = 'SecurePassword')]
        [System.Security.SecureString]$Password,

        [Parameter(Mandatory, ParameterSetName = 'PlainTextPassword')]
        [string]$PasswordPlainText,

        [Parameter(ParameterSetName = 'SecurePassword')]
        [Parameter(ParameterSetName = 'PlainTextPassword')]
        [System.Security.SecureString]$MFASecret,

        [Parameter()]
        [string]$BaseUrl
    )

    # Convert plain text password to SecureString if using that parameter set
    if ($PSCmdlet.ParameterSetName -eq 'PlainTextPassword') {
        $Password = ConvertTo-SecureString -String $PasswordPlainText -AsPlainText -Force
    }

    # Build URLs
    if ($BaseUrl) {
        $authEndpoint = "$BaseUrl/admin/auth/"
        $apiBase = "$BaseUrl/api"
    }
    else {
        $authEndpoint = "https://florecruit.com/app/$OrganizationName/admin/auth/"
        $apiBase = "https://florecruit.com/app/$OrganizationName/api"
    }

    Write-Verbose "Authenticating to: $authEndpoint"

    try {
        # Convert SecureString password to plain text for authentication
        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
        $plainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
        [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)

        # Build authentication body
        $authBody = @{
            email    = $Email
            password = $plainPassword
        }

        # Add MFA if provided
        if ($MFASecret) {
            $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($MFASecret)
            $plainMFA = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
            [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
            $authBody['mfa_secret'] = $plainMFA
        }

        # Make authentication request
        $response = Invoke-WebRequest -Uri $authEndpoint -Method POST -Body ($authBody | ConvertTo-Json) -ContentType 'application/json' -SessionVariable webSession -ErrorAction Stop

        Write-Verbose "Authentication response status: $($response.StatusCode)"

        # Extract authentication token from cookies
        # FloRecruit returns 200 OK always - check for cookie presence to verify success
        $authCookie = $null
        $cookieString = $null

        # Try to get cookie from web session first
        if ($webSession -and $webSession.Cookies) {
            $authCookie = $webSession.Cookies.GetCookies($authEndpoint) | Where-Object { $_.Name -match 'auth|token|session' } | Select-Object -First 1
        }

        if ($authCookie) {
            $cookieString = "$($authCookie.Name)=$($authCookie.Value)"
        }
        else {
            # Fallback: Try to extract from Set-Cookie header
            if ($response.Headers['Set-Cookie']) {
                $setCookieHeader = $response.Headers['Set-Cookie']
                if ($setCookieHeader -match '([^=]+)=([^;]+)') {
                    $cookieName = $Matches[1]
                    $cookieValue = $Matches[2]
                    $cookieString = "$cookieName=$cookieValue"
                }
                else {
                    throw "Authentication failed: No valid authentication cookie found in response"
                }
            }
            else {
                throw "Authentication failed: No authentication cookie found in response"
            }
        }

        # Create session object
        $Script:FloRecruitSession = [PSCustomObject]@{
            PSTypeName           = 'FloRecruit.Session'
            OrganizationName     = $OrganizationName
            Email                = $Email
            Password             = $Password
            MFASecret            = $MFASecret
            BaseUrl              = $apiBase
            AuthEndpoint         = $authEndpoint
            AuthToken            = $cookieString
            TokenExpiry          = (Get-Date).AddMinutes(30)  # Conservative estimate
            ConnectedAt          = Get-Date
            RequestCount         = 0
            RateLimitWindowStart = Get-Date
        }

        Write-Host "Successfully connected to FloRecruit as $Email" -ForegroundColor Green

        # Return session info (without credentials)
        return [PSCustomObject]@{
            PSTypeName       = 'FloRecruit.SessionInfo'
            OrganizationName = $OrganizationName
            Email            = $Email
            BaseUrl          = $apiBase
            ConnectedAt      = $Script:FloRecruitSession.ConnectedAt
        }
    }
    catch {
        Write-Error "Failed to connect to FloRecruit: $_"
        throw
    }
    finally {
        # Clear sensitive data from memory
        if ($plainPassword) { Clear-Variable -Name plainPassword -ErrorAction SilentlyContinue }
        if ($plainMFA) { Clear-Variable -Name plainMFA -ErrorAction SilentlyContinue }
    }
}