Providers/Authentik/Public/Connect-AuthentikEnvironment.ps1
|
function Connect-AuthentikEnvironment { <# .SYNOPSIS Connects to an Authentik instance and stores the connection for the session .DESCRIPTION Authentik authenticates API calls with a bearer token. There are two ways to hold one: an API token created for an administrator in the admin interface, which is the bootstrap credential, and the token of the service account New-AuthentikServiceApp creates, which is the durable one and is read from the credential record on disk. The connection is proven before it is stored. A request to the current-user endpoint has to succeed, which catches a wrong URL, a revoked token and a proxy in the way, and it names the identity the token belongs to so the connection can report who it is. Storing first and failing later would leave a broken connection behind for the next command to trip over with an unrelated message. The prefix and the email domain are fixed here for the session. Every object the seed creates carries the prefix in the form its type accepts, and every seeded user's email and every seeded application's URLs are written against the domain. .PARAMETER BaseUrl The instance URL, for example https://auth.example.com. No path. .PARAMETER ApiToken An API token, as a SecureString. The bootstrap credential. .PARAMETER ServiceAccount Connect with the service account token from the credential record. .PARAMETER CredentialPath Where the credential record is, when not in the default location. .PARAMETER VaultPassword The SecretStore password, when the token is in a vault whose password is not a default. .PARAMETER Prefix The naming prefix for everything the session creates. Must end in a hyphen or an underscore. Defaults to the module-wide ZZ-TEST-. .PARAMETER EmailDomain The domain seeded users' email addresses and seeded applications' URLs belong to. .PARAMETER PassThru Returns the connection, without its Authorization header. .OUTPUTS PSCustomObject. The connection, when -PassThru is supplied. .EXAMPLE PS> Connect-AuthentikEnvironment -BaseUrl https://auth.example.com -ApiToken $token DESCRIPTION: Connects with an administrator's API token OUTPUT: Nothing, unless -PassThru is supplied USE CASE: The first session against an instance, before a service account exists .EXAMPLE PS> Connect-AuthentikEnvironment -BaseUrl https://auth.example.com -ServiceAccount DESCRIPTION: Connects with the stored service account token OUTPUT: Nothing, unless -PassThru is supplied USE CASE: Every session after the bootstrap .NOTES Author: Jeffrey Stuhr Blog: https://www.techbyjeff.net LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', 'CredentialPath', Justification = 'A file path to a credential record, not a credential.')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ServiceAccount', Justification = 'Selects the parameter set; the set name is what the body switches on.')] [CmdletBinding(DefaultParameterSetName = 'ApiToken')] [OutputType([PSCustomObject])] param( [Parameter(Mandatory = $true)] [ValidatePattern('^https?://')] [string]$BaseUrl, [Parameter(Mandatory = $true, ParameterSetName = 'ApiToken')] [System.Security.SecureString]$ApiToken, [Parameter(Mandatory = $true, ParameterSetName = 'ServiceAccount')] [switch]$ServiceAccount, [Parameter(ParameterSetName = 'ServiceAccount')] [string]$CredentialPath, [Parameter(ParameterSetName = 'ServiceAccount')] [System.Security.SecureString]$VaultPassword, [Parameter()] [ValidatePattern('^[A-Za-z0-9][A-Za-z0-9_-]*[-_]$')] [string]$Prefix = $script:TestEnvironmentDefaultPrefix, [Parameter()] [ValidateNotNullOrEmpty()] [string]$EmailDomain = 'authentiklab.example.com', [Parameter()] [switch]$PassThru ) $normalisedUrl = $BaseUrl.TrimEnd('/') $sharedMarker = Get-TestSeedMarker -Prefix $Prefix $token = $null $authType = $PSCmdlet.ParameterSetName $resolvedCredentialPath = $null $identityHint = $null if ($authType -eq 'ApiToken') { $token = ConvertFrom-TestSecureString -SecureString $ApiToken } else { $resolvedCredentialPath = Get-AuthentikCredentialPath -BaseUrl $normalisedUrl -Path $CredentialPath $credential = Import-AuthentikCredential -Path $resolvedCredentialPath -VaultPassword $VaultPassword $token = $credential.Token $identityHint = $credential.Username } if ([string]::IsNullOrWhiteSpace($token)) { Write-Error 'The token is empty.' -ErrorAction Stop return } $candidate = @{ BaseUrl = $normalisedUrl AuthorizationHeader = "Bearer $token" AuthType = $authType Prefix = $sharedMarker.Prefix EmailDomain = $EmailDomain.TrimStart('@') SeedTag = $sharedMarker.Tag SeedMarker = '[{0}]' -f $sharedMarker.Tag CredentialPath = $resolvedCredentialPath Identity = $identityHint ConnectedAt = Get-Date } # Proven before it is stored. The current-user endpoint needs nothing but a valid token # and says whose it is. try { $me = Invoke-AuthentikRequest -Method GET -Path '/core/users/me/' -Connection $candidate if ($me -and $me.user -and $me.user.username) { $candidate.Identity = [string]$me.user.username } } catch { throw (New-Object System.Exception( "Could not authenticate to $normalisedUrl as $authType`: $($_.Exception.Message)", $_.Exception)) } $script:AuthentikConnection = $candidate Write-Verbose "Connected to $normalisedUrl as $($candidate.Identity), seeding under $($candidate.Prefix) on $($candidate.EmailDomain)" if ($PassThru) { return [PSCustomObject]@{ PSTypeName = 'AuthentikConnection' BaseUrl = $candidate.BaseUrl AuthType = $candidate.AuthType Identity = $candidate.Identity Prefix = $candidate.Prefix EmailDomain = $candidate.EmailDomain SeedTag = $candidate.SeedTag CredentialPath = $candidate.CredentialPath ConnectedAt = $candidate.ConnectedAt } } } |