Private/Auth.psm1
|
#!/usr/bin/env pwsh using namespace System using namespace System.Text using namespace System.Collections.Generic #Requires -Modules clihelper.xconvert class AuthStrategy { AuthStrategy() {} [string] GetAuthString() { return $null } } class BasicAuthStrategy : AuthStrategy { [pscredential]$credentials BasicAuthStrategy([string]$username, [securestring]$password) { $this.credentials = [pscredential]::new($username, $($password | xconvert ToSecurestring)) } [string] GetAuthString() { $raw = "{0}:{1}" -f $this.credentials.UserName, $this.credentials.GetNetworkCredential().Password $bytes = [Text.Encoding]::UTF8.GetBytes($raw) return 'Basic ' + [Convert]::ToBase64String($bytes) } } class TokenAuthStrategy : AuthStrategy { [string] $Token TokenAuthStrategy([string]$token) { $this.Token = $token } [string] GetAuthString() { return 'Bearer ' + $this.Token } } class Base64Url { static [string] Encode([byte[]]$bytes) { if ($null -eq $bytes) { return '' } return [Convert]::ToBase64String($bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_') } static [byte[]] Decode([string]$value) { if ([string]::IsNullOrEmpty($value)) { return @() } $s = $value.Replace('-', '+').Replace('_', '/') switch ($s.Length % 4) { 2 { $s += '==' ; break } 3 { $s += '='; break } } return [Convert]::FromBase64String($s) } } #region JWT class BaseJwt { [string] $Secret [hashtable] $Headers [hashtable] $Payload BaseJwt([string]$secret) { $this.Secret = $secret $this.Headers = @{ alg = 'HS256'; typ = 'JWT' } $this.Payload = @{} } hidden [string] ToBase64Url([object]$obj) { $json = ConvertTo-Json $obj -Compress -Depth 20 return [Base64Url]::Encode([Encoding]::UTF8.GetBytes($json)) } [string] ToJwt() { $headerSegment = $this.ToBase64Url($this.Headers) $payloadSegment = $this.ToBase64Url($this.Payload) $signingInput = "$headerSegment.$payloadSegment" $hmac = [System.Security.Cryptography.HMACSHA256]::new([Encoding]::UTF8.GetBytes($this.Secret)) try { $sigBytes = $hmac.ComputeHash([Encoding]::UTF8.GetBytes($signingInput)) $signature = [Base64Url]::Encode($sigBytes) return "$signingInput.$signature" } finally { $hmac.Dispose() } } } class AccessToken : BaseJwt { [string] $AccountSid [string] $ApiKeySid [string] $Identity [int] $Ttl = 3600 [List[hashtable]] $Grants AccessToken([string]$accountSid, [string]$apiKeySid, [string]$apiKeySecret) : base($apiKeySecret) { $this.AccountSid = $accountSid $this.ApiKeySid = $apiKeySid $this.Grants = [List[hashtable]]::new() } [void] AddGrant([hashtable]$grant) { if ($null -ne $grant) { $this.Grants.Add($grant) } } [string] ToJwt() { $now = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds() $grantPayload = @{} foreach ($g in $this.Grants) { foreach ($k in $g.Keys) { $grantPayload[$k] = $g[$k] } } if (![string]::IsNullOrWhiteSpace($this.Identity)) { $grantPayload['identity'] = $this.Identity } $this.Payload = @{ jti = "$($this.ApiKeySid)-$now" iss = $this.ApiKeySid sub = $this.AccountSid iat = $now exp = $now + $this.Ttl grants = $grantPayload } return ([BaseJwt]$this).ToJwt() } } class ChatGrant { static [hashtable] Create([string]$serviceSid) { return @{ chat = @{ service_sid = $serviceSid } } } } class VoiceGrant { static [hashtable] Create([string]$outgoingApplicationSid) { return @{ voice = @{ outgoing = @{ application_sid = $outgoingApplicationSid } } } } } class VideoGrant { static [hashtable] Create([string]$room) { return @{ video = @{ room = $room } } } } class PlaybackGrant { static [hashtable] Create() { return @{ playback = $true } } } #endregion JWT |