MFA.ps1

## MFA functions utilizing provisioning API

# Mar 3rd 2020
function Set-UserMFA
{
    <#
        .SYNOPSIS
        Sets user's MFA settings
 
        .DESCRIPTION
        Sets user's MFA settings using Provisioning API
     
        .Parameter AccessToken
        Access Token of the user accessing Azure Active Directory to find the given user to get the SID
 
        .Parameter UserPrincipalName
        User's principal name.
 
        .Parameter State
        State of user's MFA: Disabled, Enabled, or Enforced.
 
        .Parameter StartTime
        Remembers devices issued after the given time. Note! Applied only if State equals Enabled or Enfoced.
 
        .Parameter PhoneNumber
        User's phone number used for MFA. Must in the following format "+CCC NNNNN" where CCC is country code and NNNN the phone number without leading zero.
 
        .Parameter AlternativePhoneNumber
        User's alternative phone number used for MFA. Must in the following format "+CCC NNNNN" where CCC is country code and NNNN the phone number without leading zero.
 
        .Parameter Email
        User's phone number used for MFA. Should be correct email address.
 
        .Parameter DefaultMethod
        User's default MFA method: PhoneAppNotification, PhoneAppOTP, or OneWaySMS. TwoWayVoiceOffice and TwoWayVoiceMobile can be set but won't work anymore.
 
        .Example
        PS C:\>$at=Get-AADIntAccessTokenForAADGraph
        PS C:\>Set-AADIntUserMFA -AccessToken $at -UserPrincipalName user@company.com -PhoneNumber "+1 123456789" -DefaultMethod PhoneAppNotification
    #>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        $UserPrincipalName,
        [Parameter(Mandatory=$False)]
        [ValidateSet('Disabled','Enabled','Enforced')]
        $State,
        [Parameter(Mandatory=$False)]
        [ValidateSet('PhoneAppOTP','PhoneAppNotification','OneWaySMS','TwoWayVoiceOffice','TwoWayVoiceMobile')]
        $DefaultMethod,
        [Parameter(Mandatory=$False)]
        [DateTime]$StartTime=(Get-Date),
        [Parameter(Mandatory=$False)]
        [String]$PhoneNumber,
        [Parameter(Mandatory=$False)]
        [String]$AlternativePhoneNumber,
        [Parameter(Mandatory=$False)]
        [String]$Email
    )
    Process
    {
        # Validation for phone numbers
        function valPho
        {
            Param([String]$PhoneNumber)
            if(![String]::IsNullOrEmpty($PhoneNumber))
            {
                $regex="^[+]\d{1,3} [1-9]\d{1,11}$" # 1-3 digits (country code), space, non-zero digit, and 1 to 11 digits.
                return [regex]::Match($PhoneNumber,$regex).success
            }
            else
            {
                return $true
            }
        }

        # Check the phone numbers
        if(!((valPho $PhoneNumber) -and (valPho $AlternativePhoneNumber)))
        {
            Write-Error 'Invalid phone number format! Use the following format: "+CCC NNNNNNN" where CCC is the country code and NNNN the phonenumber without the leading zero.'
            return
        }
        
        
        $command="SetUser"

        # Get from cache if not provided
        $AccessToken = Get-AccessTokenFromCache($AccessToken)

        # Convert time to text
        $startText = $StartTime.ToUniversalTime().toString("yyyy-MM-ddTHH:mm:ss+00:00").Replace(".",":")

        # Set StrongAuthenticationRequirements
        $StrongAuthenticationRequirements="<c:StrongAuthenticationRequirements/>"
        if([string]::IsNullOrEmpty($State))
        {
            $StrongAuthenticationRequirements='<c:StrongAuthenticationRequirements i:nil="true"/>'
        }
        elseif($State -ne "Disabled")
        {
            $StrongAuthenticationRequirements=@"
                <c:StrongAuthenticationRequirements>
                    <c:StrongAuthenticationRequirement>
                        $(Add-CElement -Parameter "RelyingParty" -Value "*")
                        $(Add-CElement -Parameter "RememberDevicesNotIssuedBefore" -Value "$startText")
                        $(Add-CElement -Parameter "State" -Value "$State")
                    </c:StrongAuthenticationRequirement>
                </c:StrongAuthenticationRequirements>
"@

        }

        # Set the default method
        $StrongAuthenticationMethods='<c:StrongAuthenticationMethods i:nil="true"/>'
        if(![String]::IsNullOrEmpty($DefaultMethod))
        {
            $StrongAuthenticationMethods=@"
            <c:StrongAuthenticationMethods>
                <c:StrongAuthenticationMethod>
                    <c:IsDefault>$($DefaultMethod.Equals("PhoneAppOTP").ToString().ToLower())</c:IsDefault>
                    <c:MethodType>PhoneAppOTP</c:MethodType>
                </c:StrongAuthenticationMethod>
                <c:StrongAuthenticationMethod>
                    <c:IsDefault>$($DefaultMethod.Equals("PhoneAppNotification").ToString().ToLower())</c:IsDefault>
                    <c:MethodType>PhoneAppNotification</c:MethodType>
                </c:StrongAuthenticationMethod>
                <c:StrongAuthenticationMethod>
                    <c:IsDefault>$($DefaultMethod.Equals("OneWaySMS").ToString().ToLower())</c:IsDefault>
                    <c:MethodType>OneWaySMS</c:MethodType>
                </c:StrongAuthenticationMethod>
                <c:StrongAuthenticationMethod>
                    <c:IsDefault>$($DefaultMethod.Equals("TwoWayVoiceOffice").ToString().ToLower())</c:IsDefault>
                    <c:MethodType>TwoWayVoiceOffice</c:MethodType>
                </c:StrongAuthenticationMethod>
                <c:StrongAuthenticationMethod>
                    <c:IsDefault>$($DefaultMethod.Equals("TwoWayVoiceMobile").ToString().ToLower())</c:IsDefault>
                    <c:MethodType>TwoWayVoiceMobile</c:MethodType>
                </c:StrongAuthenticationMethod>
            </c:StrongAuthenticationMethods>
"@

        }
        
        # Create the body for setting MFA
        $request_elements=@"
            <b:User xmlns:c="http://schemas.datacontract.org/2004/07/Microsoft.Online.Administration">
                $StrongAuthenticationMethods
                <c:StrongAuthenticationPhoneAppDetails i:nil="true"/>
                <c:StrongAuthenticationProofupTime i:nil="true"/>
                $StrongAuthenticationRequirements
                <c:StrongAuthenticationUserDetails>
                    $(Add-CElement -Parameter "AlternativePhoneNumber" -Value "$AlternativePhoneNumber")
                    $(Add-CElement -Parameter "Email" -Value "$Email")
                    <c:OldPin i:nil="true"/>
                    $(Add-CElement -Parameter "PhoneNumber" -Value "$PhoneNumber")
                    <c:Pin i:nil="true"/>
                </c:StrongAuthenticationUserDetails>
                $(Add-CElement -Parameter "UserPrincipalName" -Value "$UserPrincipalName")
            </b:User>
"@


        # Create the envelope and call the API
        $response=Call-ProvisioningAPI(Create-Envelope $AccessToken $command $request_elements)

        # Get the results
        $results = Parse-SOAPResponse($Response)

        # Return
        $results
    }
}

# Mar 3rd 2020
function Get-UserMFA
{
    <#
        .SYNOPSIS
        Gets user's MFA settings
 
        .DESCRIPTION
        Gets user's MFA settings using Provisioning API
     
        .Parameter AccessToken
        Access Token of the user accessing Azure Active Directory to find the given user to get the SID
 
        .Parameter UserPrincipalName
        User's principal name.
 
        .Example
        PS C:\>$at=Get-AADIntAccessTokenForAADGraph
        PS C:\>Get-AADIntUserMFA -AccessToken $at -UserPrincipalName user@company.com
 
        UserPrincipalName : user@company.com
        State : Enforced
        PhoneNumber : +1 123456789
        AlternativePhoneNumber : +358 123456789
        Email : someone@hotmail.com
        DefaultMethod : OneWaySMS
        Pin :
        OldPin :
        StartTime :
    #>


    [cmdletbinding()]
    Param(
        [Parameter(Mandatory=$False)]
        [String]$AccessToken,
        [Parameter(Mandatory=$True)]
        $UserPrincipalName
    )
    Process
    {
        # Get the user
        $user = Get-UserByUpn -AccessToken $AccessToken -UserPrincipalName $UserPrincipalName

        # Get the details and requirements
        $details = $user.StrongAuthenticationUserDetails
        $requirements = $user.StrongAuthenticationRequirements
        $appDetails = $user.StrongAuthenticationPhoneAppDetails
        
        # Construct the attributes hashtable
        $attributes = [ordered]@{
            "UserPrincipalName" = $UserPrincipalName
            "State" = "Disabled"
            "PhoneNumber" = $details.PhoneNumber
            "AlternativePhoneNumber" = $details.AlternativePhoneNumber
            "Email" = $details.Email
            "DefaultMethod" =""
            "Pin" = $details.Pin
            "OldPin" = $details.OldPin
            "StartTime" = $null
            "AppAuthenticationType" = $null
            "AppDeviceId" = $null
            "AppDeviceName" = $null
            "AppDeviceTag" = $null
            "AppDeviceToken" = $null
            "AppId" = $null
            "AppNotificationType" = $null
            "AppOathSecretKey" = $null
            "AppOathTokenTimeDrift" = $null
            "AppPhoneAppVersion" = $null
            "AppTimeInterval" = $null
        }
        if(![string]::IsNullOrEmpty($requirements))
        {
            $attributes["State"]=$requirements.StrongAuthenticationRequirement.State
            $attributes["StartTime"]=[DateTime]$requirements.StrongAuthenticationRequirement.RememberDevicesNotIssuedBefore
        }

        if(![string]::IsNullOrEmpty($appDetails))
        {
            $app=$appDetails.StrongAuthenticationPhoneAppDetail
            $attributes["AppAuthenticationType"]=$app.AuthenticationType
            $attributes["AppDeviceId"]=$app.DeviceId
            $attributes["AppDeviceName"]=$app.DeviceName
            $attributes["AppDeviceTag"]=$app.DeviceTag
            $attributes["AppDeviceToken"]=$app.DeviceToken
            $attributes["AppId"]=$app.Id
            $attributes["AppNotificationType"]=$app.NotificationType
            $attributes["AppOathTokenTimeDrift"]=$app.OathTokenTimeDrift
            $attributes["AppPhoneAppVersion"]=$app.PhoneAppVersion
            $attributes["AppTimeInterval"]=$app.TimeInterval

        }

            

        # Get the default method
        foreach($method in $user.StrongAuthenticationMethods.StrongAuthenticationMethod)
        {
            if($method.IsDefault.equals("true"))
            {
                $attributes["DefaultMethod"]=$method.Methodtype
            }
        }

        # Return
        New-Object PSObject -Property $attributes
    }
}