Authentication.ps1 - convert to type "PowerShellUniversal.AuthenticationMethod"

Product: PowerShell Universal
Version: 2.2.1

V 2.2.1 - new install from MSI file runnning as a Service.

We are trying to update the authentication.ps1 script and getting this in the logs.

2021-09-03T07:00:27.2304968-05:00 [ERR] Failed to read configuration file. authentication.ps1 (acf13523)
System.Exception: Error while deserializing. Cannot convert the “Security.AuthenticationResult” value of type “Security.AuthenticationResult” to type “PowerShellUniversal.AuthenticationMethod”.
at UniversalAutomation.PowerShellSerializer1.Deserialize(String script, IDatabase database, IConfigurationService configService) in D:\a\universal\universal\src\UniversalAutomation\Services\PowerShellSerializer.cs:line 36 at UniversalAutomation.Services.ConfigurationScript1.ReadAsync(Boolean initialSync) in D:\a\universal\universal\src\UniversalAutomation\Services\Configuration\ConfigurationScript.cs:line 156

This is the current configuration (shortened for space) from: Security > Authentication > Form > Details
param([PSCredential]$Credential)
if ($Credential.UserName -eq ‘Admin’) {
New-PSUAuthenticationResult -Success -UserName ‘Admin’
} else { New-PSUAuthenticationResult -ErrorMessage ‘Bad username or password’ }

Our test script C:\ProgramData\UniversalAutomation\Repository.universal\authentication.ps1
param([PSCredential]$Credential)
if ($Credential.UserName -eq ‘Admin’) {
New-PSUAuthenticationResult -Success -UserName ‘Admin’
} elseif ($Credential.UserName -eq ‘Test’) {
New-PSUAuthenticationResult -Success -UserName ‘Tester’
} else {
New-PSUAuthenticationResult -ErrorMessage ‘Bad username or password’
}

When we copy our file to the C:\ProgramData\UniversalAutomation\Repository.universal\authentication.ps1 and then restart PowerShellUniversal Service, the webpage won’t load any pages.
We then need to delete the authentication.ps1 file and restart services again in order to login.

CLI testing type of cmdlet:
(New-PSUAuthenticationResult -Success -UserName asdf).GetType() | fl FullName
FullName : Security.AuthenticationResult

How do we get to type “PowerShellUniversal.AuthenticationMethod” from the authentication.ps1 script?

The examples here do not work

Security - PowerShell Universal

1 Like

Did you manage to figure this one out? Hitting the same error myself last night with 2.3.0 using New-PSUAuthenticationResult

Reverted to the method I had working on an old dashboard and that works, but looks nothing like the examples in the documentation (safely generic snippet :slight_smile: )

$Result = [Security.AuthenticationResult]::new()
if ($Credential.UserName -eq 'Admin') {
    #Maintain the out of box admin user
    $Result.Success = $true
    $Result.UserNme = $Credential.UserName
}

Really I want to be able to use New-PSAuthenticationResult so I can add claims to it using New-PSUAuthorizationClaim, something like this:

#Use API to get user's roles
        $roles = Invoke-RestMethod -uri $myrolesapi-Method POST -Body $rolesquery # returns list of role names
        #Set claims for each role to make them available as roles
        New-PSUAuthenticationResult -UserName $Credential.UserName -Success -Claims {
            $roles | Where-Object { $_ -like "Role-PSU-$($env:WEBSITE_ENVIRONMENT)*" } | ForEach-Object {
                New-PSUAuthorizationClaim -Type Role -Value $_
            }
        }

but it always seems to throw the type mismatch error:

Error while deserializing. Cannot convert the “Security.AuthenticationResult” value of type “Security.AuthenticationResult” to type “PowerShellUniversal.AuthenticationMethod”.

I think you might be running into a PowerShell module mismatch. Can you verify the versions of the Universal module you have installed on this machine? It may be loading an older module into the newer PSU server.

I think this will do it:

Get-Module Universal -ListAvailable

Was on 2.2.1 (new install when we tested) - using same module version when I posted.
Since then, upgraded to 2.3.0 along with the module and changed to Azure AD Auth - thereby skipping this problem. However, the cmdlet still getting same object type from newer module.

(New-PSUAuthenticationResult -Success).GetType().FullName
Security.AuthenticationResult

Won’t be trying this in Universal since using Azure AD Auth now.

1 Like

Oh, I think I know what’s happening here and I think it’s a problem with the documentation.

The example in the docs kind of assume that you are entering this into the editor within the Security settings.

But if you look at what is actually in the authentication.ps1, you’ll see you need to call Set-PSUAuthentication. This is the editor from the Settings \ Configurations page.

Mine’s installed via CI - downloads the MSI direct and deploys as ZIP to an app service which then goes into readonly, so it should be a pretty out-of-the-box installation.

Having a bit of trouble getting it to return all the UD modules in a readable way. The one problem I find with app services is debugging them at times like this :crazy_face:

I think I could probably do what I want by just adding group_claims, but I’ve got too many people with too many memberships.

Oh - good catch @adam !

I’ll give that a try in a moment!

Definite progress - that’s now authenticating properly.

Hit another slightly weird one - I’ve got an Invoke-RestMethod that pulls an array of roles from an API - if I put the command into roles.ps1 it works fine - but obviously it calls my API once for every role.

I’ve tried putting it in authentication.ps1 and using New-PSUAuthorizationClaim to set claims, but can’t get it to work - weirdly, it never even seems to reach out to my API.

Tried hitting a really simple endpoint with a GET and no body just to make sure my code wasn’t screwing up somehow, but nothing ever hits it. Put the same Invoke-RestMethod line inside a role in roles.ps1 and I see all the requests hitting the API.

What am I missing?

It seemingly authenticates me fine and I can’t see it throwing any errors… but nothing ever hits my endpoint so it never gets any role names to throw into the ForEach.

Set-PSUAuthenticationMethod -ScriptBlock {

    param(
        [PSCredential]$Credential
    )

    $RolesUri = 'https://myapiurl'
    $rolesbody = @{
        UserID = $Credential.UserName
    } | ConvertTo-Json

    #Use API to get user's Azure AD group membership
    $groups = Invoke-RestMethod -uri $RolesUri -Method POST -Body $rolesbody
    $psuroles = $groups | Where-Object { $_ -like "psu-role*" }
    if ($Credential.UserName -eq 'Admin') {
        #Maintain the out of box admin user
        New-PSUAuthenticationResult -UserName 'Admin' -Success
    }
    else {
        #Set claims for each rolename to make them available to roles.ps1
        New-PSUAuthenticationResult -UserName $Credential.UserName -Success -Claims {
            $psuroles | ForEach-Object {
                New-PSUAuthorizationClaim -Type Role -Value $_
            }
        }
    }
}

Is it possible that it’s throwing some sort of non-terminating error?

You might be able to check the errors by doing: $error | Out-File C:\temp\errors.txt

Could’ve been, I guess - I’m still finding my feet logging with app services - most areas are read-only after a ZIP deployment.

The same line of code copied and pasted into roles.ps1 worked fine, so I’d be surprised - even put tried the whole thing in plain text so I knew it wasn’t a variable going awry. If I find some time to have a play I’ll see if I can find any more out.

Anyway - I realised my group claims size problem isn’t a problem if I set my app registration to only set claims for groups assigned to it. Means my users need direct memberships, but it feels like the neatest way to do it all round and I can lose the external API call :slight_smile: