Skip to content

Commit 43589bf

Browse files
adding "-AT PoP" option to "Set-MgGraphOptions"
Adding AT PoP skeleton (#2511) * adding "-AT PoP" option to "Set-MgGraphOptions" --------- AT PoP Version 1 Fehintolaobafemi/methodanduri (#2751) * Making changes to how httpmethod and uri is processed --------- Fixing and updating the docs resolving build break fixing build issues related to docs resolving PR comments updating nuget packages removing unnecessary string assignment Resolving PR comments reverting test csproj changes
1 parent 37d1157 commit 43589bf

19 files changed

+293
-26
lines changed

Diff for: docs/AT-Pop.md

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Microsoft Graph PowerShell SDK: Access Token Proof of Possession (AT PoP) Capability
2+
3+
## Overview
4+
5+
This README provides comprehensive details on the Access Token Proof of Possession (AT PoP) functionality introduced in the Microsoft Graph PowerShell SDK. This feature enhances security by binding tokens to specific HTTP methods and URIs, ensuring they are used only for their intended purposes.
6+
7+
## Table of Contents
8+
9+
- [Key Features](#key-features)
10+
- [Installation](#installation)
11+
- [Configuration](#configuration)
12+
- [Usage Examples](#usage-examples)
13+
- [References](#references)
14+
15+
## Key Features
16+
17+
- **Access Token Proof of Possession (AT PoP)**: This feature binds tokens to specific HTTP methods and URIs, preventing misuse of tokens by ensuring they are used only for the intended HTTP requests.
18+
- **Updated Dependencies**: Compatibility improvements with recent library changes.
19+
- **Enhanced Token Acquisition Options**: Users can now specify the HTTP method and URI during token acquisition to further secure token usage.
20+
21+
### Token acquisition behaviors
22+
23+
| Condition | Unbound (default) | Bound (PoP) |
24+
|-----------|-----------|-----------|
25+
| First sign-in | New token, interactive| New token, interactive |
26+
| Existing token, same URI | No new token, silent | No new token, silent |
27+
| Existing token, different URI | No new token, silent | New token, silent |
28+
| Existing expired token, below max token refreshes | New token, silent | New token, silent |
29+
| Existing expired token, exceeded max refreshes | New token, interactive | New token, interactive |
30+
31+
## Installation
32+
33+
To install the Microsoft Graph PowerShell SDK with the latest updates, use the following command:
34+
35+
```powershell
36+
Install-Module -Name Microsoft.Graph -AllowClobber -Force
37+
```
38+
39+
Ensure you are using the latest version to access the AT PoP functionality.
40+
41+
## Configuration
42+
43+
### Enabling Access Token Proof of Possession
44+
45+
To enable AT PoP, configure the Microsoft Graph SDK options as follows:
46+
47+
```powershell
48+
Set-MgGraphOption -EnableATPoP $true
49+
50+
Connect-MgGraph
51+
```
52+
53+
This configuration ensures that the acquired token is only valid for the specified HTTP method and URI.
54+
55+
## Usage Examples
56+
57+
### Example 1:
58+
59+
```powershell
60+
Set-MgGraphOption -EnableATPoP $true
61+
62+
Connect-MgGraph
63+
64+
Invoke-MgGraphRequest -Method GET https://graph.microsoft.com/v1.0/me -Debug
65+
```
66+
67+
### Example 2:
68+
69+
```powershell
70+
Set-MgGraphOption -EnableATPoP $true
71+
72+
Connect-MgGraph
73+
74+
Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/me/sendMail" -Method POST -Debug
75+
```
76+
77+
## References
78+
79+
This README provides a detailed guide on the new AT PoP functionality, offering users the ability to secure their token usage effectively. If you have any questions or need further assistance, please refer to the official [Microsoft Graph PowerShell SDK documentation](https://docs.microsoft.com/en-us/powershell/microsoftgraph/).

Diff for: docs/authentication.md

+20
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,26 @@ When using `-AccessToken`, we won't have access to the refresh token and the cli
112112

113113
Before using the provided `-AccessToken` to get Microsoft Graph resources, customers should ensure that the access token has the necessary scopes/ permissions needed to access/modify a resource.
114114

115+
### Access Token Proof of Possession (AT PoP)
116+
117+
AT PoP is a security mechanism that binds an access token to a cryptographic key that only the token requestor has. This prevents unauthorized use of the token by malicious actors. AT PoP enhances data protection, reduces token replay attacks, and enables fine-grained authorization policies.
118+
119+
Note: AT PoP requires Web Account Manager (WAM) to function.
120+
121+
Microsoft Graph PowerShell module supports AT PoP in the following scenario:
122+
123+
- To enable AT PoP on supported devices
124+
125+
```PowerShell
126+
Set-MgGraphOption -EnableATPoP $true
127+
```
128+
129+
- To disable AT PoP on supported devices
130+
131+
```PowerShell
132+
Set-MgGraphOption -EnableATPoP $false
133+
```
134+
115135
## Web Account Manager (WAM)
116136

117137
WAM is a Windows 10+ component that acts as an authentication broker allowing the users of an app benefit from integration with accounts known to Windows, such as the account already signed into an active Windows session.

Diff for: src/Authentication/Authentication.Core/Common/GraphSession.cs

+5
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ public class GraphSession : IGraphSession
5656
/// </summary>
5757
public IGraphOption GraphOption { get; set; }
5858

59+
/// <summary>
60+
/// Temporarily stores the user's Graph request details such as Method and Uri. Essential as part of the Proof of Possession efforts.
61+
/// </summary>
62+
public IGraphRequestPopContext GraphRequestPopContext { get; set; }
63+
5964
/// <summary>
6065
/// Represents a collection of Microsoft Graph PowerShell meta-info.
6166
/// </summary>

Diff for: src/Authentication/Authentication.Core/Interfaces/IGraphOptions.cs

+1-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@
22
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
33
// ------------------------------------------------------------------------------
44

5-
using System;
6-
using System.Security;
7-
using System.Security.Cryptography.X509Certificates;
8-
95
namespace Microsoft.Graph.PowerShell.Authentication
106
{
117
public interface IGraphOption
128
{
139
bool EnableWAMForMSGraph { get; set; }
10+
bool EnableATPoPForMSGraph { get; set; }
1411
}
1512
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// ------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
3+
// ------------------------------------------------------------------------------
4+
5+
using Azure.Core;
6+
using Azure.Identity;
7+
using System;
8+
using System.Net.Http;
9+
10+
namespace Microsoft.Graph.PowerShell.Authentication
11+
{
12+
public interface IGraphRequestPopContext
13+
{
14+
Uri Uri { get; set; }
15+
HttpMethod HttpMethod { get; set; }
16+
AccessToken AccessToken { get; set; }
17+
PopTokenRequestContext PopTokenContext { get; set; }
18+
Request Request { get; set; }
19+
InteractiveBrowserCredential PopInteractiveBrowserCredential { get; set; }
20+
}
21+
}

Diff for: src/Authentication/Authentication.Core/Interfaces/IGraphSession.cs

+1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ public interface IGraphSession
1212
IDataStore DataStore { get; set; }
1313
IRequestContext RequestContext { get; set; }
1414
IGraphOption GraphOption { get; set; }
15+
IGraphRequestPopContext GraphRequestPopContext { get; set; }
1516
}
1617
}

Diff for: src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<Import Project="$(MSBuildThisFileDirectory)..\..\..\Repo.props" />
33
<PropertyGroup>
44
<LangVersion>9.0</LangVersion>
55
<TargetFrameworks>netstandard2.0;net6.0;net472</TargetFrameworks>
66
<RootNamespace>Microsoft.Graph.PowerShell.Authentication.Core</RootNamespace>
7-
<Version>2.18.0</Version>
7+
<Version>2.12.0</Version>
88
</PropertyGroup>
99
<PropertyGroup>
1010
<EnableNETAnalyzers>true</EnableNETAnalyzers>
1111
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
1212
</PropertyGroup>
1313
<ItemGroup>
14-
<PackageReference Include="Azure.Identity" Version="1.11.4" />
15-
<PackageReference Include="Azure.Identity.Broker" Version="1.1.0" />
16-
<PackageReference Include="Microsoft.Graph.Core" Version="3.1.13" />
14+
<PackageReference Include="Azure.Identity" Version="1.12.0-beta.3" />
15+
<PackageReference Include="Azure.Identity.Broker" Version="1.2.0-beta.1" />
16+
<PackageReference Include="Microsoft.Graph.Core" Version="3.1.12" />
17+
<PackageReference Include="Microsoft.Identity.Client" Version="4.61.3" />
18+
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.61.3" />
1719
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
1820
</ItemGroup>
1921
<Target Name="CopyFiles" AfterTargets="Build">

Diff for: src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs

+58-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// ------------------------------------------------------------------------------
44
using Azure.Core;
55
using Azure.Core.Diagnostics;
6+
using Azure.Core.Pipeline;
67
using Azure.Identity;
78
using Azure.Identity.Broker;
89
using Microsoft.Graph.Authentication;
@@ -14,6 +15,8 @@
1415
using System.Globalization;
1516
using System.IO;
1617
using System.Linq;
18+
using System.Net.Http;
19+
using System.Net.Http.Headers;
1720
using System.Security.Cryptography.X509Certificates;
1821
using System.Threading;
1922
using System.Threading.Tasks;
@@ -113,23 +116,43 @@ private static async Task<InteractiveBrowserCredential> GetInteractiveBrowserCre
113116
{
114117
if (authContext is null)
115118
throw new AuthenticationException(ErrorConstants.Message.MissingAuthContext);
116-
var interactiveOptions = IsWamSupported() ? new InteractiveBrowserCredentialBrokerOptions(WindowHandleUtlities.GetConsoleOrTerminalWindow()) : new InteractiveBrowserCredentialOptions();
119+
var interactiveOptions = IsWamSupported() ?
120+
new InteractiveBrowserCredentialBrokerOptions(WindowHandleUtlities.GetConsoleOrTerminalWindow()) :
121+
new InteractiveBrowserCredentialOptions();
117122
interactiveOptions.ClientId = authContext.ClientId;
118123
interactiveOptions.TenantId = authContext.TenantId ?? "common";
119124
interactiveOptions.AuthorityHost = new Uri(GetAuthorityUrl(authContext));
120125
interactiveOptions.TokenCachePersistenceOptions = GetTokenCachePersistenceOptions(authContext);
121126

127+
var interactiveBrowserCredential = new InteractiveBrowserCredential(interactiveOptions);
128+
if (GraphSession.Instance.GraphOption.EnableATPoPForMSGraph)
129+
{
130+
GraphSession.Instance.GraphRequestPopContext.PopTokenContext = await CreatePopTokenRequestContext(authContext);
131+
GraphSession.Instance.GraphRequestPopContext.PopInteractiveBrowserCredential = interactiveBrowserCredential;
132+
}
133+
122134
if (!File.Exists(Constants.AuthRecordPath))
123135
{
124136
AuthenticationRecord authRecord;
125-
var interactiveBrowserCredential = new InteractiveBrowserCredential(interactiveOptions);
126137
if (IsWamSupported())
127138
{
128-
authRecord = await Task.Run(() =>
139+
// Adding a scenario to account for Access Token Proof of Possession
140+
if (GraphSession.Instance.GraphOption.EnableATPoPForMSGraph)
129141
{
130-
// Run the thread in MTA.
131-
return interactiveBrowserCredential.Authenticate(new TokenRequestContext(authContext.Scopes), cancellationToken);
132-
});
142+
authRecord = await Task.Run(() =>
143+
{
144+
// Run the thread in MTA.
145+
return interactiveBrowserCredential.AuthenticateAsync(GraphSession.Instance.GraphRequestPopContext.PopTokenContext, cancellationToken);
146+
});
147+
}
148+
else
149+
{
150+
authRecord = await Task.Run(() =>
151+
{
152+
// Run the thread in MTA.
153+
return interactiveBrowserCredential.Authenticate(new TokenRequestContext(authContext.Scopes), cancellationToken);
154+
});
155+
}
133156
}
134157
else
135158
{
@@ -446,5 +469,34 @@ public static Task DeleteAuthRecordAsync()
446469
File.Delete(Constants.AuthRecordPath);
447470
return Task.CompletedTask;
448471
}
472+
473+
private static async Task<PopTokenRequestContext> CreatePopTokenRequestContext(IAuthContext authContext)
474+
{
475+
// Creating a httpclient that would handle all pop calls
476+
Uri popResourceUri = GraphSession.Instance.GraphRequestPopContext.Uri ?? new Uri("https://graph.microsoft.com/beta/organization");
477+
HttpClient popHttpClient = new(new HttpClientHandler());
478+
479+
// Find the nonce in the WWW-Authenticate header in the response.
480+
var popMethod = GraphSession.Instance.GraphRequestPopContext.HttpMethod ?? HttpMethod.Get;
481+
var popResponse = await popHttpClient.SendAsync(new HttpRequestMessage(popMethod, popResourceUri));
482+
483+
// Refresh token logic --- start
484+
var popPipelineOptions = new HttpPipelineOptions(new PopClientOptions()
485+
{
486+
487+
});
488+
489+
var _popPipeline = HttpPipelineBuilder.Build(popPipelineOptions, new HttpPipelineTransportOptions());
490+
GraphSession.Instance.GraphRequestPopContext.Request = _popPipeline.CreateRequest();
491+
GraphSession.Instance.GraphRequestPopContext.Request.Method = RequestMethod.Parse(popMethod.Method.ToUpper());
492+
GraphSession.Instance.GraphRequestPopContext.Request.Uri.Reset(popResourceUri);
493+
494+
// Refresh token logic --- end
495+
var popContext = new PopTokenRequestContext(authContext.Scopes, isProofOfPossessionEnabled: true, proofOfPossessionNonce: WwwAuthenticateParameters.CreateFromAuthenticationHeaders(popResponse.Headers, "Pop").Nonce, request: GraphSession.Instance.GraphRequestPopContext.Request);
496+
return popContext;
497+
}
498+
}
499+
internal class PopClientOptions : ClientOptions
500+
{
449501
}
450502
}

Diff for: src/Authentication/Authentication/Cmdlets/InvokeMgGraphRequest.cs

+2
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,8 @@ private async Task ProcessRecordAsync()
10261026
try
10271027
{
10281028
PrepareSession();
1029+
GraphSession.Instance.GraphRequestPopContext.Uri = Uri;
1030+
GraphSession.Instance.GraphRequestPopContext.HttpMethod = GetHttpMethod(Method);
10291031
var client = HttpHelpers.GetGraphHttpClient();
10301032
ValidateRequestUri();
10311033
using (var httpRequestMessage = GetRequest(client, Uri))

Diff for: src/Authentication/Authentication/Cmdlets/SetMgGraphOption.cs

+8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public class SetMgGraphOption : PSCmdlet
1313
{
1414
[Parameter]
1515
public bool EnableLoginByWAM { get; set; }
16+
17+
[Parameter]
18+
public bool EnableATPoP { get; set; }
1619

1720
protected override void BeginProcessing()
1821
{
@@ -27,6 +30,11 @@ protected override void ProcessRecord()
2730
GraphSession.Instance.GraphOption.EnableWAMForMSGraph = EnableLoginByWAM;
2831
WriteDebug($"Signin by Web Account Manager (WAM) is {(EnableLoginByWAM ? "enabled" : "disabled")}.");
2932
}
33+
if (this.IsParameterBound(nameof(EnableATPoP)))
34+
{
35+
GraphSession.Instance.GraphOption.EnableATPoPForMSGraph = EnableATPoP;
36+
WriteDebug($"Access Token Proof of Posession (AT-PoP) is {(EnableATPoP ? "enabled" : "disabled")}.");
37+
}
3038
File.WriteAllText(Constants.GraphOptionsFilePath, JsonConvert.SerializeObject(GraphSession.Instance.GraphOption, Formatting.Indented));
3139
}
3240

Diff for: src/Authentication/Authentication/Common/GraphSessionInitializer.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ internal static GraphSession CreateInstance(IDataStore dataStore = null)
4747
{
4848
DataStore = dataStore ?? new DiskDataStore(),
4949
RequestContext = new RequestContext(),
50-
GraphOption = graphOptions ?? new GraphOption()
50+
GraphOption = graphOptions ?? new GraphOption(),
51+
GraphRequestPopContext = new GraphRequestPopContext()
5152
};
5253
}
5354
/// <summary>

0 commit comments

Comments
 (0)