|
function Connect-DeviceAuthorization { |
|
[CmdletBinding()] |
|
param( |
|
|
|
[Parameter( |
|
HelpMessage = "Specify an Application ClientID defaults to the ClientID for 'AzureAD'" |
|
)] |
|
[string] $ClientID = '1b730954-1685-4b74-9bfd-dac224a7b894', |
|
|
|
[Parameter( |
|
HelpMessage = "Specify a TenantID, defaults to 'common'" |
|
)] |
|
[string] $TenantID = 'common', |
|
|
|
[Parameter( |
|
Mandatory, |
|
HelpMessage = "Specify a Service Endpoint for Azure" |
|
)] |
|
[string] |
|
$Resource |
|
) |
|
|
|
begin { |
|
$null = [Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime] |
|
$passwordVault = New-Object Windows.Security.Credentials.PasswordVault |
|
} |
|
|
|
process { |
|
# Check if there is a cached access token |
|
|
|
try { |
|
$cachedToken = $passwordVault.Retrieve("PSDeviceAuth", $Resource).Password | ConvertFrom-Json |
|
if (([DateTime]$cachedToken.Expires) -gt [dateTime]::Now) { |
|
return $cachedToken |
|
} |
|
} |
|
catch { |
|
|
|
} |
|
|
|
$DeviceCodeParameters = @{ |
|
Method = 'POST' |
|
Uri = "https://login.microsoftonline.com/$TenantID/oauth2/devicecode" |
|
Body = @{ |
|
client_id = $ClientId |
|
resource = $Resource |
|
} |
|
} |
|
|
|
$DeviceCodeRequest = Invoke-RestMethod @DeviceCodeParameters -UseDefaultCredentials |
|
|
|
Write-Host $DeviceCodeRequest.message -ForegroundColor Green |
|
|
|
# Set Authentication Timeout |
|
$TokenExpiration = [DateTime]::Now.AddSeconds($DeviceCodeRequest.expires_in) |
|
|
|
$TokenParameters = @{ |
|
Method = 'POST' |
|
Uri = "https://login.microsoftonline.com/$TenantId/oauth2/token" |
|
Body = @{ |
|
grant_type = "urn:ietf:params:oauth:grant-type:device_code" |
|
code = $DeviceCodeRequest.device_code |
|
client_id = $ClientId |
|
} |
|
} |
|
|
|
while ($true) { |
|
# https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code#expected-errors |
|
if ([DateTime]::Now -gt $TokenExpiration) { |
|
Write-Host "Sign in authorization timed out." -ForegroundColor Red |
|
break |
|
} |
|
# Specify Polling time |
|
Start-Sleep -Seconds $DeviceCodeRequest.interval |
|
|
|
try { |
|
$TokenRequest = Invoke-RestMethod @TokenParameters |
|
} |
|
catch { |
|
$exception = $_.ErrorDetails.Message | ConvertFrom-Json |
|
if ($exception.error -ne "authorization_pending") { |
|
throw $_ |
|
} |
|
continue |
|
} |
|
|
|
break |
|
} |
|
|
|
# Store the data. |
|
|
|
$Token = [PSCustomObject][Ordered]@{ |
|
Expires = [datetime]::Now.AddSeconds($TokenRequest.expires_in) |
|
AccessToken = $TokenRequest.access_token |
|
RefreshToken = $TokenRequest.refresh_token |
|
} |
|
|
|
|
|
$vault = New-Object Windows.Security.Credentials.PasswordVault |
|
$vault.Add( |
|
[Windows.Security.Credentials.PasswordCredential]::new( |
|
"PSDeviceAuth", |
|
$TokenRequest.Resource, |
|
($Token | ConvertTo-Json) |
|
) |
|
) |
|
|
|
# Return token that we store |
|
return $Token |
|
} |
|
|
|
end { |
|
|
|
} |
|
} |
|
|
|
|
|
|
|
|
|
#Connect-DeviceAuthorization -Resource 'https://graph.windows.net/' |