Skip to content

Commit

Permalink
ADDED: ADB2C.csproj updated with additional package references and pr…
Browse files Browse the repository at this point in the history
…operties.

ADDED: ADB2C.http file added, containing a GET request to the ADB2C host address.
ADDED: ADB2C.sln file added project and solutionDED: MeController.cs file added, containing two controller methods for GET and configuration and middleware launch profiles for added, containing appsettings file added, containing configuration settings for Azure AD B2C, OpenID and allowed hosts.
  • Loading branch information
dgmjr committed Nov 28, 2023
1 parent 5af4771 commit e1ced9a
Show file tree
Hide file tree
Showing 8 changed files with 395 additions and 1 deletion.
19 changes: 18 additions & 1 deletion ADB2C.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<Title>Active Directory B2C Identity Test</Title>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
<UserSecretsId>3c5831a3-10fe-4af0-8251-3b31ae14f177</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<PackageReference Include="Microsoft.AspNetCore.Session" />
<PackageReference Include="Microsoft.Identity.Web" />
<PackageReference Include="Microsoft.Identity.Web.MicrosoftGraph" />
<PackageReference Include="Microsoft.Identity.Web.UI" />
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Swashbuckle.AspNetCore" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions ADB2C.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@ADB2C_HostAddress = http://localhost:5288

GET {{ADB2C_HostAddress}}/weatherforecast/
Accept: application/json

###
42 changes: 42 additions & 0 deletions ADB2C.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Microsoft Visual Studio Solution File, Format Version 12.00
#
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B283EBC2-E01F-412D-9339-FD56EF114549}"
ProjectSection(SolutionItems) = preProject
..\Directory.Build.props = ..\Directory.Build.props
..\Directory.Build.targets = ..\Directory.Build.targets
..\..\..\global.json = ..\..\..\global.json
..\..\..\Packages\Versions.Local.props = ..\..\..\Packages\Versions.Local.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ADB2C", "ADB2C.csproj", "{1F05D736-EA65-4E82-9F14-EFDB324B2F63}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Local|Any CPU = Local|Any CPU
Debug|Any CPU = Debug|Any CPU
Testing|Any CPU = Testing|Any CPU
Staging|Any CPU = Staging|Any CPU
Production|Any CPU = Production|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Local|Any CPU.ActiveCfg = Local|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Local|Any CPU.Build.0 = Local|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Testing|Any CPU.ActiveCfg = Testing|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Testing|Any CPU.Build.0 = Testing|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Staging|Any CPU.ActiveCfg = Staging|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Staging|Any CPU.Build.0 = Staging|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Production|Any CPU.ActiveCfg = Local|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Production|Any CPU.Build.0 = Local|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F05D736-EA65-4E82-9F14-EFDB324B2F63}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E62FAA4C-9868-487A-AA4E-8DF41E52132B}
EndGlobalSection
EndGlobal
54 changes: 54 additions & 0 deletions Controllers/MeController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
namespace ADB2C.Controllers;

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web.Resource;
using Microsoft.Identity.Web;
using Microsoft.Graph;

[Authorize(Policy = "alloweduser")]
[ApiController]
public class MeController(GraphServiceClient graphClient) : ControllerBase
{
private readonly GraphServiceClient _graphClient = graphClient;

[HttpGet]
[Route("api/me")]
[AuthorizeForScopes(ScopeKeySection = "AzureAdB2C:Scopes")]
public async Task<User> Get()
{
var graphUser = await _graphClient.Me
.Request()
// .Select(
// u =>
// new
// {
// u.AboutMe,
// u.AgeGroup,
// u.AdditionalData,
// u.Activities,
// u.City,
// u.Interests,
// u.Identities
// }
// )
.GetAsync();
return graphUser;
}

[HttpPut]
[Route("api/me")]
[AuthorizeForScopes(ScopeKeySection = "AzureAdB2C:Scopes")]
public async Task<User> Set([FromBody] System.Security.Claims.Claim claim)
{
var graphUser = await _graphClient.Me
.Request()
.UpdateAsync(
new User
{
AdditionalData = new Dictionary<string, object> { { claim.Type, claim.Value } }
}
);
return graphUser;
}
}
160 changes: 160 additions & 0 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.AspNetCore.Authentication.Cookies;
using Azure.Extensions.AspNetCore.Configuration.Secrets;
using Azure.Identity;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Logging;
using Constants = Microsoft.Identity.Web.Constants;

var builder = WebApplication.CreateBuilder(args);

var initialScopes = builder.Configuration[
$"{nameof(MicrosoftGraphOptions)}:{nameof(MicrosoftGraphOptions.Scopes)}"
]?.Split(' ');

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOptions();

// Add services to the container.
builder.Services
.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection(Constants.AzureAdB2C))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(builder.Configuration.GetSection(nameof(MicrosoftGraphOptions)))
.AddDistributedTokenCaches(); //we might need to change this to scale the app

// builder.Services
// .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
// .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection(Constants.AzureAdB2C))
// .EnableTokenAcquisitionToCallDownstreamApi()
// .AddMicrosoftGraph(builder.Configuration.GetSection(nameof(MicrosoftGraphOptions)))
// .AddInMemoryTokenCaches();

builder.Services.AddSwaggerGen(c => c.SwaggerDoc("v1", new() { Title = "ADB2C", Version = "v1" }));

// .AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
// .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection(Constants.AzureAd))
// .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
// .AddMicrosoftGraph(builder.Configuration.GetSection(nameof(MicrosoftGraphOptions)))
// .AddDistributedTokenCaches(); //we might need to change this to scale the app

builder.Services.AddMicrosoftGraph();

Check failure on line 45 in Program.cs

View workflow job for this annotation

GitHub Actions / build

'IServiceCollection' does not contain a definition for 'AddMicrosoftGraph' and the best extension method overload 'MicrosoftGraphExtensions.AddMicrosoftGraph(MicrosoftIdentityAppCallsWebApiAuthenticationBuilder, string, string)' requires a receiver of type 'Microsoft.Identity.Web.MicrosoftIdentityAppCallsWebApiAuthenticationBuilder'

Check failure on line 45 in Program.cs

View workflow job for this annotation

GitHub Actions / build

'IServiceCollection' does not contain a definition for 'AddMicrosoftGraph' and the best extension method overload 'MicrosoftGraphExtensions.AddMicrosoftGraph(MicrosoftIdentityAppCallsWebApiAuthenticationBuilder, string, string)' requires a receiver of type 'Microsoft.Identity.Web.MicrosoftIdentityAppCallsWebApiAuthenticationBuilder'
builder.Services.AddProblemDetails();
builder.Services.AddHealthChecks();
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
IdentityModelEventSource.LogCompleteSecurityArtifact = true;

Check failure on line 49 in Program.cs

View workflow job for this annotation

GitHub Actions / build

'IdentityModelEventSource' does not contain a definition for 'LogCompleteSecurityArtifact'

Check failure on line 49 in Program.cs

View workflow job for this annotation

GitHub Actions / build

'IdentityModelEventSource' does not contain a definition for 'LogCompleteSecurityArtifact'
IdentityModelEventSource.ShowPII = true;

builder.Services.Configure<CookieAuthenticationOptions>(
CookieAuthenticationDefaults.AuthenticationScheme,
options => options.AccessDeniedPath = "/AccessDenied"
);

// builder.Services.Configure<JwtBearerOptions>(
// JwtBearerDefaults.AuthenticationScheme,
// options =>
// {
// options.Events = new JwtBearerEvents { OnAuthenticationFailed = AuthenticationFailed };
// }
// );

builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});

//if the access to the webapp needs to be limited to a specific role, set the role in the appsettings.json
//if the role is not set, the webapp will be open to all authenticated users
//this allows you to show a friendly access denied message with optional instructions for your users
//how to get access if they want to or if they can
//this access policy is set on the index.html and on the controller through [Authorize(Policy = "alloweduser")] attribute
var requiredUserRoleForAccess = builder.Configuration["AzureAdB2C:AllowedUsersRole"];
if (!IsNullOrEmpty(requiredUserRoleForAccess))

Check failure on line 77 in Program.cs

View workflow job for this annotation

GitHub Actions / build

The name 'IsNullOrEmpty' does not exist in the current context

Check failure on line 77 in Program.cs

View workflow job for this annotation

GitHub Actions / build

The name 'IsNullOrEmpty' does not exist in the current context
{
builder.Services
.AddAuthorizationBuilder()
.AddDefaultPolicy(
"alloweduser",
policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireRole(requiredUserRoleForAccess);

Check warning on line 86 in Program.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'roles' in 'AuthorizationPolicyBuilder AuthorizationPolicyBuilder.RequireRole(params string[] roles)'.

Check warning on line 86 in Program.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'roles' in 'AuthorizationPolicyBuilder AuthorizationPolicyBuilder.RequireRole(params string[] roles)'.
}
);
}
else
{
builder.Services
.AddAuthorizationBuilder()
.AddDefaultPolicy("alloweduser", policy => policy.RequireAuthenticatedUser());
}

builder.Services.Configure<SessionOptions>(
builder.Configuration.GetSection(nameof(SessionOptions))
);
builder.Services.AddSession();

// options =>
// {
// options.IdleTimeout = TimeSpan.FromMinutes(1); //You can set Time
// options.Cookie.IsEssential = true;
// options.Cookie.SameSite = SameSiteMode.None;
// options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
// options.Cookie.HttpOnly = true;
// });

builder.Services.AddRazorPages().AddMicrosoftIdentityUI();

builder.Services.AddHttpClient(); // use iHttpFactory as best practice, should be easy to use extra retry and hold off policies in the future

// The following lines code instruct the asp.net core middleware to use the data in the "roles" claim in the Authorize attribute and User.IsInrole()
// See https://docs.microsoft.com/aspnet/core/security/authorization/roles?view=aspnetcore-2.2 for more info.
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
builder.Configuration.GetSection(nameof(OpenIdConnectOptions))
);

var app = builder.Build();

// this setting is used when you use tools like ngrok or reverse proxies like nginx which connect to http://localhost
// if you don't set this setting the sign-in redirect will be http instead of https
app.UseForwardedHeaders(
new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedProto }
);

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI();

Check failure on line 135 in Program.cs

View workflow job for this annotation

GitHub Actions / build

There is no argument given that corresponds to the required parameter 'setupAction' of 'SwaggerUIBuilderExtensions.UseSwaggerUI(IApplicationBuilder, Action<SwaggerUIOptions>)'

Check failure on line 135 in Program.cs

View workflow job for this annotation

GitHub Actions / build

There is no argument given that corresponds to the required parameter 'setupAction' of 'SwaggerUIBuilderExtensions.UseSwaggerUI(IApplicationBuilder, Action<SwaggerUIOptions>)'
}
else
{
app.UseHsts();
app.UseExceptionHandler("/Error");
}

app.UseSession();
app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseCookiePolicy(new CookiePolicyOptions { Secure = CookieSecurePolicy.Always });

app.MapRazorPages();
app.MapControllers();

// generate an api-key on startup that we can use to validate callbacks
env.SetEnvironmentVariable("API-KEY", guid.NewGuid().ToString());

Check failure on line 158 in Program.cs

View workflow job for this annotation

GitHub Actions / build

The name 'env' does not exist in the current context

Check failure on line 158 in Program.cs

View workflow job for this annotation

GitHub Actions / build

The name 'guid' does not exist in the current context

app.Run();
41 changes: 41 additions & 0 deletions Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:35859",
"sslPort": 44365
}
},
"profiles": {
// "http": {
// "commandName": "Project",
// "dotnetRunMessages": true,
// "launchBrowser": true,
// "launchUrl": "swagger",
// "applicationUrl": "https://localhost:5288",
// "environmentVariables": {
// "ASPNETCORE_ENVIRONMENT": "Development"
// }
// },
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7157;http://localhost:5288",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
8 changes: 8 additions & 0 deletions appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Loading

0 comments on commit e1ced9a

Please sign in to comment.