private/Invoke-OneTokenRequest.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
function Invoke-OneTokenRequest {
    [CmdletBinding()]
    param()

    begin {
        Write-Verbose "[$(Get-Date)] [BEGIN ] $($MyInvocation.MyCommand)"
    }

    process {
        $TokenBody = @{
            grant_type = "client_credentials"
            scope = "https://graph.microsoft.com/.default"
            client_id = $OneShortcutSession.AzureAdApp.ClientId
        }

        $TokenHeader = @{}

        if ($OneShortcutSession.AzureAdApp.ClientCertificate) {
            $CertificateBase64Hash = ([System.Convert]::ToBase64String($OneShortcutSession.AzureAdApp.ClientCertificate.GetCertHash()))

            $JwtHeader = @{
                alg = "RS256"
                typ = "JWT"
                x5t = ($CertificateBase64Hash -replace "\+", "-" -replace "/", "_" -replace "=")
            }

            $JwtStartDate = ((Get-Date "1970-01-01T00:00:00Z").ToUniversalTime())
            $JwtExpTimeSpan = ((New-TimeSpan -Start $JwtStartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds)
            $JwtExp = [math]::Round($JwtExpTimeSpan, 0)
            $JwtNotBeforeExpTimeSpan = ((New-TimeSpan -Start $JwtStartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds)
            $JwtNotBeforeExp = [math]::Round($JwtNotBeforeExpTimeSpan, 0)

            $JwtPayload = @{
                aud = "https://login.microsoftonline.com/$($OneShortcutSession.AzureAdApp.TenantId)/oauth2/token"
                exp = $JwtExp
                iss = $OneShortcutSession.AzureAdApp.ClientId
                jti = [guid]::NewGuid()
                nbf = $JwtNotBeforeExp
                sub = $OneShortcutSession.AzureAdApp.ClientId
            }

            $JwtHeaderToByte = [System.Text.Encoding]::UTF8.GetBytes(($JwtHeader | ConvertTo-Json))
            $EncHeader = [System.Convert]::ToBase64String($JwtHeaderToByte)
            $JwtPayloadToByte = [System.Text.Encoding]::UTF8.GetBytes(($JwtPayload | ConvertTo-Json))
            $EncPayload = [System.Convert]::ToBase64String($JwtPayloadToByte)

            $Jwt = "$($EncHeader).$($EncPayload)"

            $PrivateKey = $OneShortcutSession.AzureAdApp.ClientCertificate.PrivateKey
            $RsaPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1
            $HashAlg = [Security.Cryptography.HashAlgorithmName]::SHA256
            
            $Signature = ([Convert]::ToBase64String($PrivateKey.SignData([System.Text.Encoding]::UTF8.GetBytes($Jwt), $HashAlg, $RsaPadding)) -replace "\+", "-" -replace "/", "_" -replace "=")

            $Jwt = "$($Jwt).$($Signature)"

            $TokenHeader.Authorization = "Bearer $($Jwt)"

            $TokenBody.client_assertion = $JWT
            $TokenBody.client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
        } elseif ($OneShortcutSession.AzureAdApp.ClientSecret) {
            $client_secret = [System.Net.NetworkCredential]::new("", $OneShortcutSession.AzureAdApp.ClientSecret).Password
            $TokenBody.client_secret = $client_secret
        }

        $TokenRequest = @{
            Uri = "https://login.microsoftonline.com/$($OneShortcutSession.AzureAdApp.TenantId)/oauth2/v2.0/token"
            Headers = $TokenHeader
            ContentType = "application/x-www-form-urlencoded"
            Method = [Microsoft.PowerShell.Commands.WebRequestMethod]::Post
            Body = $TokenBody
        } 

        $Token = @{
            Response = @{
                StatusCode = $null
                Message = $null
            }
            Data = @{
                AccessToken = $null
                ExpiresOn = $null
            }
        }

        try {
            $TokenResponse = (Invoke-WebRequest @TokenRequest)
            $TokenContent = (ConvertFrom-Json $([String]::new($TokenResponse.Content)))

            $Token.Response.StatusCode = $TokenResponse.StatusCode
            $Token.Data.AccessToken = $TokenContent.access_token
            $Token.Data.ExpiresOn = (Get-Date).AddMinutes(59)
        } catch {
            $Token.Response.StatusCode = $_.Exception.Response.StatusCode.value__
            $Token.Response.Message = $_.Exception.Message
        }

        return ([pscustomobject] $Token)
    }

    end {
        Write-Verbose "[$(Get-Date)] [END ] $($MyInvocation.MyCommand)"
    }
}