Skip to content
Merged
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
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[*]
indent_style = space
indent_size = 2
insert_final_newline = true

[*.{cs,cshtml}]
indent_size = 4
dotnet_diagnostic.SA1101.severity = silent
dotnet_diagnostic.SA1513.severity = silent
dotnet_diagnostic.SA1600.severity = silent
dotnet_diagnostic.CS1591.severity = silent
6 changes: 3 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build user-auth",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/user-auth/GraphTutorial/bin/Debug/net8.0/GraphTutorial.dll",
"program": "${workspaceFolder}/user-auth/GraphTutorial/bin/Debug/net9.0/GraphTutorial.dll",
"args": [],
"cwd": "${workspaceFolder}/user-auth/GraphTutorial",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
Expand All @@ -26,7 +26,7 @@
"request": "launch",
"preLaunchTask": "build app-auth",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/app-auth/GraphAppOnlyTutorial/bin/Debug/net8.0/GraphAppOnlyTutorial.dll",
"program": "${workspaceFolder}/app-auth/GraphAppOnlyTutorial/bin/Debug/net9.0/GraphAppOnlyTutorial.dll",
"args": [],
"cwd": "${workspaceFolder}/app-auth/GraphAppOnlyTutorial",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
Expand All @@ -39,4 +39,4 @@
"request": "attach"
}
]
}
}
21 changes: 16 additions & 5 deletions app-auth/GraphAppOnlyTutorial/GraphAppOnlyTutorial.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<!-- cSpell:ignore stylecop contentfiles buildtransitive -->

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>2275df63-2eb2-47b9-a11f-710535686d4b</UserSecretsId>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
Expand All @@ -14,12 +17,20 @@
</None>
</ItemGroup>

<ItemGroup>
<AdditionalFiles Include="../../stylecop.json" Link="stylecop.json" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.17.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
<PackageReference Include="Microsoft.Graph" Version="5.93.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.10" />
<PackageReference Include="Microsoft.Graph" Version="5.95.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

</Project>
87 changes: 43 additions & 44 deletions app-auth/GraphAppOnlyTutorial/GraphHelper.cs
Original file line number Diff line number Diff line change
@@ -1,87 +1,86 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

using Azure.Core;
using Azure.Identity;
using Microsoft.Graph;
using Microsoft.Graph.Models;

class GraphHelper
namespace GraphAppOnlyTutorial;

public class GraphHelper
{
// <AppOnlyAuthConfigSnippet>
/* <AppOnlyAuthConfigSnippet> */
// Settings object
private static Settings? _settings;
private static Settings? settings;

// App-ony auth token credential
private static ClientSecretCredential? _clientSecretCredential;
private static ClientSecretCredential? clientSecretCredential;

// Client configured with app-only authentication
private static GraphServiceClient? _appClient;
private static GraphServiceClient? appClient;

public static void InitializeGraphForAppOnlyAuth(Settings settings)
{
_settings = settings;
GraphHelper.settings = settings;

// Ensure settings isn't null
_ = settings ??
throw new System.NullReferenceException("Settings cannot be null");
throw new NullReferenceException("Settings cannot be null");

_settings = settings;
GraphHelper.settings = settings;

if (_clientSecretCredential == null)
{
_clientSecretCredential = new ClientSecretCredential(
_settings.TenantId, _settings.ClientId, _settings.ClientSecret);
}
clientSecretCredential ??= new ClientSecretCredential(
GraphHelper.settings.TenantId, GraphHelper.settings.ClientId, GraphHelper.settings.ClientSecret);

if (_appClient == null)
{
_appClient = new GraphServiceClient(_clientSecretCredential,
// Use the default scope, which will request the scopes
// configured on the app registration
new[] {"https://graph.microsoft.com/.default"});
}
appClient ??= new GraphServiceClient(
clientSecretCredential,
/* Use the default scope, which will request the scopes
configured on the app registration */
["https://graph.microsoft.com/.default"]);
}
// </AppOnlyAuthConfigSnippet>
/* </AppOnlyAuthConfigSnippet> */

// <GetAppOnlyTokenSnippet>
/* <GetAppOnlyTokenSnippet> */
public static async Task<string> GetAppOnlyTokenAsync()
{
// Ensure credential isn't null
_ = _clientSecretCredential ??
throw new System.NullReferenceException("Graph has not been initialized for app-only auth");
_ = clientSecretCredential ??
throw new NullReferenceException("Graph has not been initialized for app-only auth");

// Request token with given scopes
var context = new TokenRequestContext(new[] {"https://graph.microsoft.com/.default"});
var response = await _clientSecretCredential.GetTokenAsync(context);
var context = new TokenRequestContext(["https://graph.microsoft.com/.default"]);
var response = await clientSecretCredential.GetTokenAsync(context);
return response.Token;
}
// </GetAppOnlyTokenSnippet>
/* </GetAppOnlyTokenSnippet> */

// <GetUsersSnippet>
/* <GetUsersSnippet> */
public static Task<UserCollectionResponse?> GetUsersAsync()
{
// Ensure client isn't null
_ = _appClient ??
throw new System.NullReferenceException("Graph has not been initialized for app-only auth");
_ = appClient ??
throw new NullReferenceException("Graph has not been initialized for app-only auth");

return _appClient.Users.GetAsync((config) =>
return appClient.Users.GetAsync((config) =>
{
// Only request specific properties
config.QueryParameters.Select = new[] { "displayName", "id", "mail" };
// Get at most 25 results
/* Only request specific properties */
config.QueryParameters.Select = ["displayName", "id", "mail"];
/* Get at most 25 results */
config.QueryParameters.Top = 25;
// Sort by display name
config.QueryParameters.Orderby = new[] { "displayName" };
/* Sort by display name */
config.QueryParameters.Orderby = ["displayName"];
});
}
// </GetUsersSnippet>
/* </GetUsersSnippet> */

#pragma warning disable CS1998
// <MakeGraphCallSnippet>
// This function serves as a playground for testing Graph snippets
// or other code
public async static Task MakeGraphCallAsync()
#pragma warning disable CS1998
/* <MakeGraphCallSnippet> */
/* This function serves as a playground for testing Graph snippets
or other code */
public static async Task MakeGraphCallAsync()
{
// INSERT YOUR CODE HERE
}
// </MakeGraphCallSnippet>
/* </MakeGraphCallSnippet> */
}
26 changes: 14 additions & 12 deletions app-auth/GraphAppOnlyTutorial/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

// <ProgramSnippet>
/* <ProgramSnippet> */
using GraphAppOnlyTutorial;

Console.WriteLine(".NET Graph App-only Tutorial\n");

var settings = Settings.LoadSettings();
Expand Down Expand Up @@ -29,7 +31,7 @@
choice = -1;
}

switch(choice)
switch (choice)
{
case 0:
// Exit the program
Expand All @@ -52,16 +54,16 @@
break;
}
}
// </ProgramSnippet>
/* </ProgramSnippet> */

// <InitializeGraphSnippet>
/* <InitializeGraphSnippet> */
void InitializeGraph(Settings settings)
{
GraphHelper.InitializeGraphForAppOnlyAuth(settings);
}
// </InitializeGraphSnippet>
/* </InitializeGraphSnippet> */

// <DisplayAccessTokenSnippet>
/* <DisplayAccessTokenSnippet> */
async Task DisplayAccessTokenAsync()
{
try
Expand All @@ -74,9 +76,9 @@ async Task DisplayAccessTokenAsync()
Console.WriteLine($"Error getting app-only access token: {ex.Message}");
}
}
// </DisplayAccessTokenSnippet>
/* </DisplayAccessTokenSnippet> */

// <ListUsersSnippet>
/* <ListUsersSnippet> */
async Task ListUsersAsync()
{
try
Expand Down Expand Up @@ -111,11 +113,11 @@ async Task ListUsersAsync()
Console.WriteLine($"Error getting users: {ex.Message}");
}
}
// </ListUsersSnippet>
/* </ListUsersSnippet> */

// <MakeGraphCallSnippet>
/* <MakeGraphCallSnippet> */
async Task MakeGraphCallAsync()
{
await GraphHelper.MakeGraphCallAsync();
}
// </MakeGraphCallSnippet>
/* </MakeGraphCallSnippet> */
16 changes: 10 additions & 6 deletions app-auth/GraphAppOnlyTutorial/Settings.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// Licensed under the MIT license.

// <SettingsSnippet>
/* <SettingsSnippet> */
using Microsoft.Extensions.Configuration;

namespace GraphAppOnlyTutorial;

public class Settings
{
public string? ClientId { get; set; }

public string? ClientSecret { get; set; }

public string? TenantId { get; set; }

public static Settings LoadSettings()
{
// Load settings
IConfiguration config = new ConfigurationBuilder()
// appsettings.json is required
/* appsettings.json is required */
.AddJsonFile("appsettings.json", optional: false)
// appsettings.Development.json" is optional, values override appsettings.json
/* appsettings.Development.json" is optional, values override appsettings.json */
.AddJsonFile($"appsettings.Development.json", optional: true)
// User secrets are optional, values override both JSON files
/* User secrets are optional, values override both JSON files */
.AddUserSecrets<Program>()
.Build();

return config.GetRequiredSection("Settings").Get<Settings>() ??
throw new Exception("Could not load app settings. See README for configuration instructions.");
}
}
// </SettingsSnippet>
/* </SettingsSnippet> */
13 changes: 13 additions & 0 deletions stylecop.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
"documentationRules": {
"companyName": "Microsoft Corporation",
"copyrightText": "Copyright (c) {companyName}.\nLicensed under the MIT license.",
"xmlHeader": false
},
"orderingRules": {
"usingDirectivesPlacement": "outsideNamespace"
}
}
}
Loading