CloudConnect.psm1

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
Function Get-ServiceToken {
    <#
    .SYNOPSIS
    Get a token for a given Microsoft Cloud Service
 
    .DESCRIPTION
    Returns an ADAL token for a given Microsoft Cloud Service
    Will attempt to acquire the token silently (refresh) if possible
 
    .LINK
    https://github.com/Canthv0/CloudAuthModule
 
    .OUTPUTS
    Returns a token object for the requested cloud service
 
    .EXAMPLE
    Get-ServiceToken -Service EXO
 
    Returns a token for the Exchange Online Service.
 
    #>


    
    param (
        # Parameter help description
        [Parameter(Mandatory = $true)]
        [ValidateSet("EXO","AzureGraph")]
        [string]
        $Service
    )

    # References
    # https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-token
    # https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/tree/adalv3/dev
    #

    # Ensure our ADAL types are loaded and availble
    Add-ADALType

    switch ($Service) {
        exo {
            # EXO Powershell Client ID
            $clientId = "a0c73c16-a7e3-4564-9a95-2bdf47383716" 
            # Set redirect URI for PowerShell
            $redirectUri = "urn:ietf:wg:oauth:2.0:oob"
            # Set Resource URI to EXO endpoint
            $resourceAppIdURI = "https://outlook.office365.com"
            # Set Authority to Azure AD Tenant
            $authority = "https://login.windows.net/common"

        }
        AzureGraph {
            # Azure PowerShell Client ID
            $clientId = "1950a258-227b-4e31-a9cf-717495945fc2"
            # Set redirect URI for PowerShell
            $redirectUri = "urn:ietf:wg:oauth:2.0:oob"
            # Set Resource URI to EXO endpoint
            $resourceAppIdURI = "https://graph.windows.net"
            # Set Authority to Azure AD Tenant
            $authority = "https://login.windows.net/common"            
        }
        Default { Write-Error "Service Not Implemented" -ErrorAction Stop }
    }

    # Create AuthenticationContext tied to Azure AD Tenant
    $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority

    # Create platform Options, we want it to prompt if it needs to.
    $platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Always"
    
    # Acquire token, this will place it in the token cache
    # $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters)

    Write-Debug "Looking in token cache"
    $Result = $authContext.AcquireTokenSilentAsync($resourceAppIdURI, $clientId)
    
    while ($result.IsCompleted -ne $true) { Start-Sleep -Milliseconds 500; write-debug "silent sleep" }
    
    # Check if we failed to get the token
    if (!($Result.IsFaulted -eq $false)) {
         
        Write-Debug "Acquire token silent failed"
        switch ($Result.Exception.InnerException.ErrorCode) {
            failed_to_acquire_token_silently { 
                # do nothing since we pretty much expect this to fail
                Write-Information "Cache miss, asking for credentials"
                $Result = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters)
                
                while ($result.IsCompleted -ne $true) { Start-Sleep -Milliseconds 500; write-debug "sleep" }
            }
            multiple_matching_tokens_detected {
                # we could clear the cache here since we don't have a UPN, but we are just going to move on to prompting
                Write-Information "Multiple matching entries found, asking for credentials"
                $Result = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientId, $redirectUri, $platformParameters)
                
                while ($result.IsCompleted -ne $true) { Start-Sleep -Milliseconds 500; write-debug "sleep" }
            }
            Default { Write-Error -Message "Unknown Token Error $Result.Exception.InnerException.ErrorCode" -ErrorAction Stop }
        }
    }   

    Return $Result
    
}

Function Add-ADALType {

    $path = join-path (split-path (Get-Module azuread -ListAvailable | Where-Object { $_.Version -eq '2.0.2.16' }).Path -parent) 'Microsoft.IdentityModel.Clients.ActiveDirectory.dll'
    Add-Type -Path $path
    
}

Function Get-TokenCache {
    param(
        [Switch]$Full = $false
    )

    <#
    .SYNOPSIS
    Returns the current contents of the token cache
 
    .DESCRIPTION
    Returns basic properties about the objects currently in the token cache.
    Returns the local time that the token will expire.
 
    .LINK
    https://github.com/Canthv0/CloudAuthModule
 
    .OUTPUTS
    List of the information in the token cache.
 
    .EXAMPLE
    Get-TokenCache
 
    Displays the information currently in the token cache.
 
    #>



    # Ensure our ADAL types are loaded and availble
    Add-ADALType

    $cache = [Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache]::DefaultShared
    
    if ($full){
        Return $Cache.ReadItems()
    }
    else {
        $cache.ReadItems() | Select-Object DisplayableId, Authority, ClientId, Resource, @{Name = "ExpiresOn"; Expression = { $_.ExpiresOn.localdatetime } }
    }
    
    

}