Public/Authentication.ps1

<#
.SYNOPSIS
Create a new type of Authentication.
 
.DESCRIPTION
Create a new type of Authentication, which is used to parse the Request for user credentials for validating.
 
.PARAMETER Basic
If supplied, will use the inbuilt Basic Authentication credentials retriever.
 
.PARAMETER Encoding
The Encoding to use when decoding the Basic Authorization header.
 
.PARAMETER HeaderTag
The name of the type of Basic Authentication.
 
.PARAMETER Form
If supplied, will use the inbuilt Form Authentication credentials retriever.
 
.PARAMETER UsernameField
The name of the Username Field in the payload to retrieve the username.
 
.PARAMETER PasswordField
The name of the Password Field in the payload to retrieve the password.
 
.PARAMETER Custom
If supplied, will allow you to create a Custom Authentication credentials retriever.
 
.PARAMETER ScriptBlock
The ScriptBlock to retrieve user credentials.
 
.PARAMETER ArgumentList
An array of arguments to supply to the Custom Authentication type's ScriptBlock.
 
.EXAMPLE
$basic_auth = New-PodeAuthType -Basic
 
.EXAMPLE
$form_auth = New-PodeAuthType -Form -UsernameField 'Email'
 
.EXAMPLE
$custom_auth = New-PodeAuthType -Custom -ScriptBlock { /* logic */ }
#>

function New-PodeAuthType
{
    [CmdletBinding(DefaultParameterSetName='Basic')]
    [OutputType([hashtable])]
    param (
        [Parameter(ParameterSetName='Basic')]
        [switch]
        $Basic,

        [Parameter(ParameterSetName='Basic')]
        [string]
        $Encoding = 'ISO-8859-1',

        [Parameter(ParameterSetName='Basic')]
        [string]
        $HeaderTag = 'Basic',

        [Parameter(ParameterSetName='Form')]
        [switch]
        $Form,

        [Parameter(ParameterSetName='Form')]
        [string]
        $UsernameField = 'username',

        [Parameter(ParameterSetName='Form')]
        [string]
        $PasswordField = 'password',

        [Parameter(ParameterSetName='Custom')]
        [switch]
        $Custom,

        [Parameter(Mandatory=$true, ParameterSetName='Custom')]
        [ValidateScript({
            if (Test-IsEmpty $_) {
                throw "A non-empty ScriptBlock is required for the Custom authentication type"
            }

            return $true
        })]
        [scriptblock]
        $ScriptBlock,

        [Parameter(ParameterSetName='Custom')]
        [hashtable]
        $ArgumentList
    )

    # configure the auth type
    switch ($PSCmdlet.ParameterSetName.ToLowerInvariant()) {
        'basic' {
            return @{
                ScriptBlock = (Get-PodeAuthBasicType)
                Arguments = @{
                    HeaderTag = (Protect-PodeValue -Value $HeaderTag -Default 'Basic')
                    Encoding = (Protect-PodeValue -Value $Encoding -Default 'ISO-8859-1')
                }
            }
        }

        'form' {
            return @{
                ScriptBlock = (Get-PodeAuthFormType)
                Arguments = @{
                    Fields = @{
                        Username = (Protect-PodeValue -Value $UsernameField -Default 'username')
                        Password = (Protect-PodeValue -Value $PasswordField -Default 'password')
                    }
                }
            }
        }

        'custom' {
            return @{
                ScriptBlock = $ScriptBlock
                Arguments = $ArgumentList
            }
        }
    }
}

<#
.SYNOPSIS
Adds a custom Authentication method for verifying users.
 
.DESCRIPTION
Adds a custom Authentication method for verifying users.
 
.PARAMETER Name
A unique Name for the Authentication method.
 
.PARAMETER Type
The Type to use for retrieving credentials (From New-PodeAuthType).
 
.PARAMETER ScriptBlock
The ScriptBlock defining logic that retrieves and verifys a user.
 
.PARAMETER ArgumentList
An array of arguments to supply to the Custom Authentication's ScriptBlock.
 
.EXAMPLE
New-PodeAuthType -Form | Add-PodeAuth -Name 'Main' -ScriptBlock { /* logic */ }
#>

function Add-PodeAuth
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [string]
        $Name,

        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [hashtable]
        $Type,

        [Parameter(Mandatory=$true)]
        [ValidateScript({
            if (Test-IsEmpty $_) {
                throw "A non-empty ScriptBlock is required for the authentication method"
            }

            return $true
        })]
        [scriptblock]
        $ScriptBlock,

        [Parameter()]
        [object[]]
        $ArgumentList
    )

    # ensure the name doesn't already exist
    if ($PodeContext.Server.Authentications.ContainsKey($Name)) {
        throw "Authentication method already defined: $($Name)"
    }

    # ensure the Type contains a scriptblock
    if (Test-IsEmpty $Type.ScriptBlock) {
        throw "The supplied Type for the '$($Name)' authentication method requires a valid ScriptBlock"
    }

    # add auth method to server
    $PodeContext.Server.Authentications[$Name] = @{
        Type = $Type
        ScriptBlock = $ScriptBlock
        Arguments = $ArgumentList
    }
}

<#
.SYNOPSIS
Adds the inbuilt Windows AD Authentication method for verifying users.
 
.DESCRIPTION
Adds the inbuilt Windows AD Authentication method for verifying users.
 
.PARAMETER Name
A unique Name for the Authentication method.
 
.PARAMETER Type
The Type to use for retrieving credentials (From New-PodeAuthType).
 
.PARAMETER Fqdn
A custom FQDN for the DNS of the AD you wish to authenticate against.
 
.PARAMETER Groups
An array of Group names to only allow access.
 
.PARAMETER Users
An array of Usernames to only allow access.
 
.PARAMETER NoGroups
If supplied, groups will not be retrieved for the user in AD.
 
.EXAMPLE
New-PodeAuthType -Form | Add-PodeAuthWindowsAd -Name 'WinAuth'
 
.EXAMPLE
New-PodeAuthType -Basic | Add-PodeAuthWindowsAd -Name 'WinAuth' -Groups @('Developers')
 
.EXAMPLE
New-PodeAuthType -Form | Add-PodeAuthWindowsAd -Name 'WinAuth' -NoGroups
#>

function Add-PodeAuthWindowsAd
{
    [CmdletBinding(DefaultParameterSetName='Groups')]
    param (
        [Parameter(Mandatory=$true)]
        [string]
        $Name,

        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [hashtable]
        $Type,

        [Parameter()]
        [string]
        $Fqdn = $env:USERDNSDOMAIN,

        [Parameter(ParameterSetName='Groups')]
        [string[]]
        $Groups,

        [Parameter()]
        [string[]]
        $Users,

        [Parameter(ParameterSetName='NoGroups')]
        [switch]
        $NoGroups
    )

    # Check PowerShell/OS version
    $version = $PSVersionTable.PSVersion
    if ((Test-IsUnix) -or ($version.Major -eq 6 -and $version.Minor -eq 0)) {
        throw 'Windows AD authentication is currently only supported on Windows PowerShell, and Windows PowerShell Core v6.1+'
    }

    # ensure the name doesn't already exist
    if ($PodeContext.Server.Authentications.ContainsKey($Name)) {
        throw "Windows AD Authentication method already defined: $($Name)"
    }

    # ensure the Type contains a scriptblock
    if (Test-IsEmpty $Type.ScriptBlock) {
        throw "The supplied Type for the '$($Name)' Windows AD authentication method requires a valid ScriptBlock"
    }

    # add Windows AD auth method to server
    $PodeContext.Server.Authentications[$Name] = @{
        Type = $Type
        ScriptBlock = (Get-PodeAuthInbuiltMethod -Type WindowsAd)
        Arguments = @{
            Fqdn = $Fqdn
            Users = $Users
            Groups = $Groups
            NoGroups = $NoGroups
        }
    }
}

<#
.SYNOPSIS
Remove a specific Authentication method.
 
.DESCRIPTION
Remove a specific Authentication method.
 
.PARAMETER Name
The Name of the Authentication method.
 
.EXAMPLE
Remove-PodeAuth -Name 'Login'
#>

function Remove-PodeAuth
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string]
        $Name
    )

    $PodeContext.Server.Authentications.Remove($Name) | Out-Null
}

<#
.SYNOPSIS
Clear all defined Authentication methods.
 
.DESCRIPTION
Clear all defined Authentication methods.
 
.EXAMPLE
Clear-PodeAuth
#>

function Clear-PodeAuth
{
    [CmdletBinding()]
    param()

    $PodeContext.Server.Authentications.Clear()
}

<#
.SYNOPSIS
Returns Authentication Middleware that can be used globally, or an Routes.
 
.DESCRIPTION
Returns Authentication Middleware that can be used globally, or an Routes.
 
.PARAMETER Name
The Name of the Authentication method.
 
.PARAMETER FailureUrl
The URL to redirect to when authentication fails.
 
.PARAMETER FailureMessage
An override Message to throw when authentication fails.
 
.PARAMETER SuccessUrl
The URL to redirect to when authentication succeeds.
 
.PARAMETER EnableFlash
If supplied, error messages will be added as Flash messages.
 
.PARAMETER Sessionless
If supplied, authenticated users will not be stored in sessions, and sessions will not be used.
 
.PARAMETER AutoLogin
If supplied, navigating to a login page with a valid session will redirect to the SuccessUrl. Otherwise the login page will be displayed.
 
.PARAMETER Logout
If supplied, the current session will be purged, and the user will be redirected to the FailureUrl.
 
.EXAMPLE
Add-PodeRoute -Method Get -Path '/' -Middleware (Get-PodeAuthMiddleware -Name 'Main') -ScriptBlock { /* logic */ }
 
.EXAMPLE
Get-PodeAuthMiddleware -Name 'BasicAuth' -Sessionless | Add-PodeMiddeware -Name 'GlobalAuth'
 
.EXAMPLE
Add-PodeRoute -Method Get -Path '/login' -Middleware (Get-PodeAuthMiddleware -Name 'Main' -SuccessUrl '/' -AutoLogin) -ScriptBlock { /* logic */ }
#>

function Get-PodeAuthMiddleware
{
    [CmdletBinding()]
    [OutputType([hashtable])]
    param (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string]
        $Name,

        [Parameter()]
        [string]
        $FailureUrl,

        [Parameter()]
        [string]
        $FailureMessage,

        [Parameter()]
        [string]
        $SuccessUrl,

        [switch]
        $EnableFlash,

        [switch]
        $Sessionless,

        [switch]
        $AutoLogin,

        [switch]
        $Logout
    )

    # ensure the auth method exists
    if (!$PodeContext.Server.Authentications.ContainsKey($Name)) {
        throw "Authentication method does not exist: $($Name)"
    }

    # if we're using sessions, ensure sessions have been setup
    if (!$Sessionless -and !(Test-PodeSessionsConfigured)) {
        throw 'Sessions are required to use session persistent authentication'
    }

    # create the options
    $options = @{
        Name = $Name
        Failure = @{
            Url = $FailureUrl
            Message = $FailureMessage
            FlashEnabled = $EnableFlash
        }
        Success = @{
            Url = $SuccessUrl
        }
        Sessionless = $Sessionless
        AutoLogin = $AutoLogin
        Logout = $Logout
    }

    # return the middleware
    return (Get-PodeAuthMiddlewareScript | New-PodeMiddleware -ArgumentList $options)
}