Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save watahani/383f2aff2480e579e27127821897682a to your computer and use it in GitHub Desktop.
Save watahani/383f2aff2480e579e27127821897682a to your computer and use it in GitHub Desktop.
$clientId = '<client-id>'
$clientSecret = '<client-secret>'
$tenantId = "<your-tenant-id>"
$redirectUri='http://localhost:8000'
$scope = "openid profile offline_access"
$authorizeEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/authorize"
$tokenEndpont = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$interval = 30;
$codeChallengeMethod = "S256"
$prompt="consent"
$retryTimes = 100
# Convert bytes to base64 string
function Convert-BytesToBase64 {
[OutputType([String])]
param(
[Parameter(Mandatory = $True, ValueFromPipeline = $True)]
[byte[]]$bytes
)
process {
$b64enc = [Convert]::ToBase64String($bytes)
return $b64enc
}
}
# Bytes To Base64Url
function Convert-BytesToBase64Url {
[OutputType([String])]
param(
[Parameter(Mandatory = $True, ValueFromPipeline = $True)]
[byte[]]$bytes
)
process {
$base64string = Convert-BytesToBase64 $bytes
$base64Url = $base64String.TrimEnd('=').Replace('+', '-').Replace('/', '_');
return $base64Url
}
}
function ConvertFrom-CodeVerifier {
<#
.SYNOPSIS
Generate Code Challenge from Code Verifier String
.EXAMPLE
ConvertFrom-CodeVerifier 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk' -Method s256
#>
[OutputType([String])]
param(
[Parameter(Mandatory = $True, ValueFromPipeline = $True)]
[String]$codeVerifier,
[ValidateSet(
"plain",
"s256"
)]$Method = "s256"
)
process {
switch($Method){
"plain" {
return $codeVerifier
}
"s256" {
# https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-filehash?view=powershell-7
$stringAsStream = [System.IO.MemoryStream]::new()
$writer = [System.IO.StreamWriter]::new($stringAsStream)
$writer.write($codeVerifier)
$writer.Flush()
$stringAsStream.Position = 0
$hash = Get-FileHash -InputStream $stringAsStream | Select-Object Hash
$hex = $hash.Hash
$bytes = [byte[]]::new($hex.Length / 2)
For($i=0; $i -lt $hex.Length; $i+=2){
$bytes[$i/2] = [convert]::ToByte($hex.Substring($i, 2), 16)
}
$b64enc = [Convert]::ToBase64String($bytes)
$b64url = $b64enc.TrimEnd('=').Replace('+', '-').Replace('/', '_')
return $b64url
}
default {
throw "not supported method: $Method"
}
}
}
}
function New-Randombytes {
<#
.SYNOPSIS
Generate Code Verifier String
#>
[OutputType([String])]
param(
[int]$length = 64
)
process {
$bytes = [System.Byte[]]::new($length);
[System.Security.Cryptography.RNGCryptoServiceProvider]::new().GetBytes($bytes);
return $bytes
}
}
# https://gallery.technet.microsoft.com/JWT-Token-Decode-637cf001
function Convert-FromBase64StringWithNoPadding([string]$data)
{
$data = $data.Replace('-', '+').Replace('_', '/')
switch ($data.Length % 4)
{
0 { break }
2 { $data += '==' }
3 { $data += '=' }
default { throw New-Object ArgumentException('data') }
}
return [System.Convert]::FromBase64String($data)
}
function Decode-JWT([string]$rawToken)
{
$parts = $rawToken.Split('.');
$headers = [System.Text.Encoding]::UTF8.GetString((Convert-FromBase64StringWithNoPadding $parts[0]))
$claims = [System.Text.Encoding]::UTF8.GetString((Convert-FromBase64StringWithNoPadding $parts[1]))
$signature = (Convert-FromBase64StringWithNoPadding $parts[2])
$customObject = [PSCustomObject]@{
headers = ($headers | ConvertFrom-Json)
claims = ($claims | ConvertFrom-Json)
signature = $signature
}
Write-Verbose -Message ("JWT`r`n.headers: {0}`r`n.claims: {1}`r`n.signature: {2}`r`n" -f $headers,$claims,[System.BitConverter]::ToString($signature))
return $customObject
}
function Get-JwtTokenData
{
[CmdletBinding()]
Param
(
# Param1 help description
[Parameter(Mandatory=$true)]
[string] $Token,
[switch] $Recurse
)
if ($Recurse)
{
$decoded = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Token))
Write-Host("Token") -ForegroundColor Green
Write-Host($decoded)
$DecodedJwt = Decode-JWT -rawToken $decoded
}
else
{
$DecodedJwt = Decode-JWT -rawToken $Token
}
Write-Host("Token Values") -ForegroundColor Green
Write-Host ($DecodedJwt | Select headers,claims | ConvertTo-Json)
return $DecodedJwt
}
function Start-LocalServer {
$hostname = "http://localhost:8000/"
$listener = New-Object Net.HttpListener
$listener.Prefixes.add($hostname)
$url = $null;
try{
$listener.Start()
Write-Host "server start"
while($true){
$context = $listener.GetContext()
$request = $context.Request
$response = $context.Response
$url = $request.RawUrl
$content = [byte[]]@()
if($url.contains("code=")){
$content = [System.Text.Encoding]::UTF8.GetBytes('received authorization code');
$response.OutputStream.Write($content, 0, $content.Length)
$response.Close()
break;
} else {
$response.StatusCode = 404;
$response.OutputStream.Write($content, 0, $content.Length)
$response.Close()
}
}
} finally {
$listener.Dispose()
}
return $url;
}
$codeVerifier = ,(New-Randombytes -length 64) | Convert-BytesToBase64Url
$nonce = ,(New-Randombytes -length 64) | Convert-BytesToBase64Url
$state = ,(New-Randombytes -length 64) | Convert-BytesToBase64Url
$codeChallenge = ConvertFrom-CodeVerifier $codeVerifier;
$authparams = @{
client_id=$clientId;
redirect_uri=$redirectUri;
scope=$scope;
nonce=$nonce;
state=$state;
code_challenge=$codeChallenge;
code_challenge_method=$codeChallengeMethod;
response_type="code";
prompt=$prompt;
}
$authqueries = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
foreach($key in $authparams.Keys){
$authqueries[$key] = $authparams[$key]
}
$authUrl = $authorizeEndpoint + "?" + $authqueries.ToString()
$authUrl | clip.exe
Write-Host -ForegroundColor Yellow "Authorize Request URL cpied! Please past to browser and sign in!"
Write-Host "authUrl: $authUrl"
# $url=Read-Host "Enter Whole URL"
$url = Start-LocalServer
if($url.contains("?")){
$params = [System.Web.HttpUtility]::ParseQueryString($url.Split("?")[1])
$code = $params["code"]
$stateFromServer = $params["state"]
if($stateFromServer -ne $state){
throw "state unmatch `n originState: $satate `nserverState: $stateFromServer"
}
}
$postParams = @{
client_id = $clientId;
client_secret = $clientSecret;
redirect_uri=$redirectUri;
grant_type = 'authorization_code';
scope = "$scope";
code=$code;
code_verifier=$codeVerifier;
}
$body = (Invoke-WebRequest -Uri $tokenEndpont -Method POST -Body $postParams) | ConvertFrom-Json
$refreshToken=$body.refresh_token
Get-Date
$jwt = Decode-JWT $body.id_token
$jwt.claims
Write-Host "========================================"
$at = Decode-Jwt $body.access_token
$at.claims
Write-Host "========================================"
for($i=0; $i -lt $retryTimes; $i ++){
Sleep $interval
$body = @{
client_id = $clientId;
client_secret = $clientSecret;
redirect_uri = $redirectUri;
grant_type= 'refresh_token';
refresh_token = $refreshToken;
}
$tokenResp = Invoke-WebRequest -Uri $tokenEndpont -Method POST -ContentType 'application/x-www-form-urlencoded'` -body $body;
$body = ConvertFrom-Json $tokenResp.Content ;
Get-Date
$jwt = Decode-JWT $body.id_token
$jwt.claims
Write-Host "========================================"
$at = Decode-Jwt $body.access_token
$at.claims
Write-Host "========================================"
$refreshToken=$body.refresh_token
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment