Public/Get-GkUserAuthMethod.ps1

function Get-GkUserAuthMethod {
    <#
    .SYNOPSIS
        List the authentication methods registered on a user's account.

    .DESCRIPTION
        Reads GET /users/{id}/authentication/methods and returns one row per registered method with
        its type resolved (Microsoft Authenticator, FIDO2, phone, TAP, Windows Hello, password, ...).
        Get-GkUserMfaStatus reports the tenant-wide registration *summary*; this is the per-user
        detail behind it, for when you need to know exactly what an account can sign in with.

        Reading another user's methods requires UserAuthenticationMethod.Read.All. The narrower
        UserAuthenticationMethod.Read grants only the signed-in user's own methods, so it is not
        accepted here.

        No secret material is returned by Graph, but phone numbers and device names are: treat the
        output as sensitive.

    .PARAMETER UserId
        One or more user object IDs or userPrincipalNames. Accepts pipeline input, including by the
        UserPrincipalName / Id property so report output can be piped in.

    .PARAMETER MethodType
        Only return methods of this type (matched against the resolved MethodType column).

    .PARAMETER AsReport
        Add a ReportGeneratedUtc column.

    .EXAMPLE
        Get-GkUserAuthMethod -UserId ada@contoso.com

        Every method registered on one account.

    .EXAMPLE
        Get-GkUserMfaStatus | Where-Object { -not $_.IsMfaCapable } | Get-GkUserAuthMethod

        Inspect what the users who are not MFA-capable actually have registered.

    .EXAMPLE
        Get-GkUserAuthMethod -UserId ada@contoso.com -MethodType Fido2

    .OUTPUTS
        PSGraphKit.UserAuthMethod
    #>

    [CmdletBinding()]
    [OutputType('PSGraphKit.UserAuthMethod')]
    param(
        [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('UserPrincipalName', 'Id')]
        [string[]] $UserId,

        [string] $MethodType,

        [switch] $AsReport
    )

    begin {
        Test-GkConnection -FunctionName 'Get-GkUserAuthMethod' -Caller $PSCmdlet | Out-Null
        $now = [datetime]::UtcNow

        # Graph identifies each method only by its @odata.type; map it to something an admin reads.
        $typeNames = @{
            'microsoftAuthenticatorAuthenticationMethod' = 'MicrosoftAuthenticator'
            'phoneAuthenticationMethod'                  = 'Phone'
            'fido2AuthenticationMethod'                  = 'Fido2'
            'passwordAuthenticationMethod'               = 'Password'
            'windowsHelloForBusinessAuthenticationMethod' = 'WindowsHelloForBusiness'
            'temporaryAccessPassAuthenticationMethod'    = 'TemporaryAccessPass'
            'emailAuthenticationMethod'                  = 'Email'
            'softwareOathAuthenticationMethod'           = 'SoftwareOath'
            'x509CertificateAuthenticationMethod'        = 'X509Certificate'
            'platformCredentialAuthenticationMethod'     = 'PlatformCredential'
            'passwordlessMicrosoftAuthenticatorAuthenticationMethod' = 'PasswordlessAuthenticator'
        }
    }

    process {
        foreach ($uid in $UserId) {
            if ([string]::IsNullOrWhiteSpace($uid)) { continue }
            $enc = [uri]::EscapeDataString($uid)

            try {
                $methods = Invoke-GkGraphRequest -Uri "/users/$enc/authentication/methods" -CallerFunction 'Get-GkUserAuthMethod'
            }
            catch {
                Write-Warning "Could not read authentication methods for '$uid': $($_.Exception.Message)"
                continue
            }

            foreach ($m in $methods) {
                $odata = ([string](Get-GkDictValue $m '@odata.type')) -replace '^#microsoft\.graph\.', ''
                $type = if ($odata -and $typeNames.ContainsKey($odata)) { $typeNames[$odata] } elseif ($odata) { $odata } else { 'Unknown' }

                if ($MethodType -and $type -ne $MethodType) { continue }

                # Each method type names its label differently; take whichever is present.
                $detail = ''
                foreach ($f in 'displayName', 'phoneNumber', 'emailAddress', 'model', 'deviceTag') {
                    $v = [string](Get-GkDictValue $m $f)
                    if ($v) { $detail = $v; break }
                }

                $obj = [ordered]@{
                    PSTypeName  = 'PSGraphKit.UserAuthMethod'
                    UserId      = $uid
                    MethodType  = $type
                    Detail      = $detail
                    CreatedDateTime = ConvertTo-GkDateTime (Get-GkDictValue $m 'createdDateTime')
                    Id          = [string](Get-GkDictValue $m 'id')
                }
                if ($AsReport) { $obj['ReportGeneratedUtc'] = $now }
                [pscustomobject]$obj
            }
        }
    }
}