Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions Common/Utils/AuthUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace Microsoft.Intune.PowerShellGraphSDK
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

internal static partial class AuthUtils
{
Expand Down Expand Up @@ -49,6 +51,53 @@ internal static partial class AuthUtils
/// </summary>
internal static bool UseMsiAuth => !string.IsNullOrWhiteSpace(ManagedServiceIdentityEndpoint) && LatestAdalAuthResult == null;

/// <summary>
/// Authenticates with the currently set environment parameters and the provided client certificate identified by thumbprint.
/// </summary>
/// <param name="certificateThumbprint">The client secret</param>
/// <returns>The authentication result.</returns>
internal static SdkAuthResult AuthWithCertificateThumbprint(string certificateThumbprint)
{
// Get the environment parameters
EnvironmentParameters environmentParameters = AuthUtils.CurrentEnvironmentParameters;

// Create auth context that we will use to connect to the AAD endpoint
AuthenticationContext authContext = new AuthenticationContext(environmentParameters.AuthUrl);

// Get certificate with specified Thumbprint from "My" store
X509Certificate2 xCertificate = null;

using (X509Store xStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
xStore.Open(OpenFlags.ReadOnly);
// Get unexpired certificates with the specified name.
X509Certificate2Collection unexpiredCerts = xStore.Certificates
.Find(X509FindType.FindByTimeValid, DateTime.Now, false)
.Find(X509FindType.FindByThumbprint, certificateThumbprint, false);
if (unexpiredCerts == null)
throw new Exception($"{certificateThumbprint} certificate was not found or has expired.");
// Only return current cert.
xCertificate = unexpiredCerts
.OfType<X509Certificate2>()
.OrderByDescending(c => c.NotBefore)
.FirstOrDefault();
}

// Build clientAssertionCertificate for the request
ClientAssertionCertificate clientAssertionCertificate = new ClientAssertionCertificate(CurrentEnvironmentParameters.AppId, xCertificate);

// Acquire token for Microsoft Graph via certificate credentials from AAD
AuthenticationResult authenticationResult = authContext.AcquireTokenAsync(CurrentEnvironmentParameters.GraphBaseAddress, clientAssertionCertificate).GetAwaiter().GetResult();

// Convert the auth result into our own type
SdkAuthResult authResult = authenticationResult.ToSdkAuthResult();

// Save the auth result
AuthUtils.LatestAdalAuthResult = authResult;

return authResult;
}

/// <summary>
/// Authenticates with the currently set environment parameters and the provided client secret.
/// </summary>
Expand Down
19 changes: 19 additions & 0 deletions PowerShellCmdlets/Utils/UtilCmdlets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public class Connect : PSCmdlet
/// </summary>
private const string ParameterSetAdminConsent = "AdminConsent";

/// <summary>
/// Parameter set for triggering the app-only authentication with a client certificate.
/// </summary>
private const string ParameterSetCertificateThumbprint = "CertificateThumbprint";

/// <summary>
/// Parameter set for triggering app-only authentication.
/// </summary>
Expand Down Expand Up @@ -87,6 +92,14 @@ public class Connect : PSCmdlet
[Parameter(ParameterSetName = ParameterSetAppOnly)]
public string ClientSecret { get; set; }

/// <summary>
/// <para type="description">
/// If the certificate thumbprint is set, app-only authentication will be performed using the client ID specified by the AppId environment parameter.
/// </para>
/// </summary>
[Parameter(ParameterSetName = ParameterSetCertificateThumbprint)]
public string CertificateThumbprint { get; set; }

/// <summary>
/// <para type="description">
/// If the AdminConsent flag is set, admin consent can be granted for the currently selected AppId
Expand Down Expand Up @@ -125,6 +138,12 @@ protected override void ProcessRecord()
// App-only auth
authResult = AuthUtils.AuthWithClientCredentials(this.ClientSecret);
}
else if (this.ParameterSetName == ParameterSetCertificateThumbprint)
{
authResult = AuthUtils.AuthWithCertificateThumbprint(this.CertificateThumbprint);

}

else
{
// User auth
Expand Down
8 changes: 4 additions & 4 deletions PowerShellGraphSDK.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.0.5" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="XmlDoc2CmdletDoc" Version="0.2.12">
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.4" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="XmlDoc2CmdletDoc" Version="0.2.13">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
Expand Down Expand Up @@ -90,7 +90,7 @@
</Target>

<Target Name="GeneratePowerShellModuleManifest" AfterTargets="CopyPowerShellModuleAdditions">
<Exec Command="$(PowerShellExe) -NonInteractive -ExecutionPolicy Unrestricted -Command &quot;&amp; { $(PowerShellModuleManifestGenerationScript) -ModuleName &apos;$(PowerShellModuleName)&apos; -OutputDirectory &apos;$(OutputPath)&apos; -MainModuleRelativePath &apos;$(AssemblyName).dll&apos; }&quot;" />
<Exec Command="$(PowerShellExe) -NonInteractive -ExecutionPolicy Unrestricted -Command &quot;&amp; { $(PowerShellModuleManifestGenerationScript) -ModuleName '$(PowerShellModuleName)' -OutputDirectory '$(OutputPath)' -MainModuleRelativePath '$(AssemblyName).dll' }&quot;" />
</Target>

</Project>