diff --git a/CosNet.API/Controllers/WeatherForecastController.cs b/CosNet.API/Controllers/WeatherForecastController.cs index f7980ec1..af447c48 100644 --- a/CosNet.API/Controllers/WeatherForecastController.cs +++ b/CosNet.API/Controllers/WeatherForecastController.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace CosNet.API.Controllers { + [Authorize] [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase diff --git a/CosNet.API/CosNet.API.csproj b/CosNet.API/CosNet.API.csproj index 54c0877f..2d9ee57c 100644 --- a/CosNet.API/CosNet.API.csproj +++ b/CosNet.API/CosNet.API.csproj @@ -6,6 +6,7 @@ + all diff --git a/CosNet.API/Properties/launchSettings.json b/CosNet.API/Properties/launchSettings.json index 947f0f37..26ecab37 100644 --- a/CosNet.API/Properties/launchSettings.json +++ b/CosNet.API/Properties/launchSettings.json @@ -1,30 +1,31 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:56531", - "sslPort": 44308 + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:6001", + "sslPort": 44308 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" } - }, - "$schema": "http://json.schemastore.org/launchsettings.json", - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "weatherforecast", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "CosNet.API": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "COSNET_API_SECRET": "apisecret" }, - "CosNet.API": { - "commandName": "Project", - "launchBrowser": true, - "launchUrl": "cosplay", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:6001;http://localhost:6000" - } - } + "applicationUrl": "https://localhost:6001;http://localhost:6000" + } + } } diff --git a/CosNet.API/Startup.cs b/CosNet.API/Startup.cs index 26443b01..5c55966a 100644 --- a/CosNet.API/Startup.cs +++ b/CosNet.API/Startup.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using AutoMapper; using CosNet.API.DBContexts; +using IdentityServer4.AccessTokenValidation; using CosNet.API.Repositories; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -32,15 +33,34 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { services.AddControllers(); - services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); - services.AddTransient(); - //register the swagger generator, defining a swagger document services.AddMvc(); + services.AddCors(options => + { + options.AddPolicy( + "cosnet-cors-policy", + builder => builder.WithOrigins(Configuration.GetSection("CosNetWebUIUrl").Value) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + + services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); + services.AddSwaggerGen(c => { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "CosNet API", Version = "v1" }); + c.SwaggerDoc("v1", new OpenApiInfo { Title = "CosNet API", Version = "v1" }); }); + + services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) + .AddIdentityServerAuthentication(options => + { + options.Authority = Configuration.GetSection("CosNetIDPUrl").Value; + options.ApiName = "cosnet-api"; + options.ApiSecret = Environment.GetEnvironmentVariable("COSNET_API_SECRET"); + }); + + services.AddTransient(); services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); } @@ -54,11 +74,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseHttpsRedirection(); - app.UseCors(options => - { - options.AllowAnyOrigin(); - }); - app.UseSwagger(); app.UseSwaggerUI(c => @@ -69,6 +84,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseRouting(); + app.UseCors("cosnet-cors-policy"); + + app.UseAuthentication(); + app.UseAuthorization(); app.UseEndpoints(endpoints => diff --git a/CosNet.API/appsettings.Development.json b/CosNet.API/appsettings.Development.json index 4aae0bae..d80f1f2c 100644 --- a/CosNet.API/appsettings.Development.json +++ b/CosNet.API/appsettings.Development.json @@ -1,11 +1,12 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, - "CosNetIDPUrl": "https://localhost:7001" + "CosNetWebUIUrl": "https://localhost:5001", + "CosNetIDPUrl": "https://localhost:7001" } diff --git a/CosNet.API/appsettings.json b/CosNet.API/appsettings.json index 6fc76d07..2edd06b7 100644 --- a/CosNet.API/appsettings.json +++ b/CosNet.API/appsettings.json @@ -1,17 +1,18 @@ { - "ConnectionStrings": { - "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=CosNet;Trusted_Connection=True;MultipleActiveResultSets=true" - }, + "ConnectionStrings": { + "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=CosNet;Trusted_Connection=True;MultipleActiveResultSets=true" + }, - "CosNetIDPUrl": "https://cosnet-idp.azurewebsites.net", + "CosNetWebUIUrl": "https://cosnet.azurewebsites.net", + "CosNetIDPUrl": "https://cosnet-idp.azurewebsites.net", - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, - "AllowedHosts": "*" + "AllowedHosts": "*" } diff --git a/CosNet.IDP/Config.cs b/CosNet.IDP/Config.cs index 6d0975d9..bb4d2b4a 100644 --- a/CosNet.IDP/Config.cs +++ b/CosNet.IDP/Config.cs @@ -2,57 +2,62 @@ // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. +using IdentityServer4; using IdentityServer4.Models; using System.Collections.Generic; +using System.Configuration; namespace CosNet.IDP { - public static class Config - { - public static IEnumerable IdentityResources => - new IdentityResource[] - { - new IdentityResources.OpenId(), - new IdentityResources.Profile(), - }; - - public static IEnumerable ApiScopes => - new ApiScope[] + public static class Config + { + public static IEnumerable IdentityResources => + new IdentityResource[] + { + new IdentityResources.OpenId(), + new IdentityResources.Profile(), + }; + + public static IEnumerable ApiScopes => + new ApiScope[] + { + new ApiScope("cosnet-api") + }; + + public static IEnumerable ApiResources => + new ApiResource[] + { + new ApiResource( + "cosnet-api", + "CosNet API", + new List() { "role" }) { - new ApiScope("scope1"), - new ApiScope("scope2"), - }; - - public static IEnumerable Clients => - new Client[] + Scopes = { "cosnet-api"}, + ApiSecrets = { new Secret(System.Environment.GetEnvironmentVariable("COSNET_API_SECRET").Sha256()) } + } + }; + + public static IEnumerable Clients => + new Client[] + { + new Client { - // m2m client credentials flow client - new Client - { - ClientId = "m2m.client", - ClientName = "Client Credentials Client", - - AllowedGrantTypes = GrantTypes.ClientCredentials, - ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) }, - - AllowedScopes = { "scope1" } - }, - - // interactive client using code flow + pkce - new Client - { - ClientId = "interactive", - ClientSecrets = { new Secret("49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256()) }, - - AllowedGrantTypes = GrantTypes.Code, - - RedirectUris = { "https://localhost:44300/signin-oidc" }, - FrontChannelLogoutUri = "https://localhost:44300/signout-oidc", - PostLogoutRedirectUris = { "https://localhost:44300/signout-callback-oidc" }, - - AllowOfflineAccess = true, - AllowedScopes = { "openid", "profile", "scope2" } - }, - }; - } -} \ No newline at end of file + ClientId = "cosnetwebui", + AllowedGrantTypes = GrantTypes.Code, + RequireClientSecret = false, + AllowedCorsOrigins = { System.Environment.GetEnvironmentVariable("COSNET_WEBUI_URL") }, + RedirectUris = { $"{System.Environment.GetEnvironmentVariable("COSNET_WEBUI_URL")}/authentication/login-callback" }, + FrontChannelLogoutUri = $"{System.Environment.GetEnvironmentVariable("COSNET_WEBUI_URL")}/", + PostLogoutRedirectUris = { $"{System.Environment.GetEnvironmentVariable("COSNET_WEBUI_URL")}/" }, + + AllowOfflineAccess = true, + AllowedScopes = + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + "cosnet-api" + } + }, + }; + } +} diff --git a/CosNet.IDP/CosNet.IDP.csproj b/CosNet.IDP/CosNet.IDP.csproj index d499a74e..41d519c5 100644 --- a/CosNet.IDP/CosNet.IDP.csproj +++ b/CosNet.IDP/CosNet.IDP.csproj @@ -19,5 +19,6 @@ + - \ No newline at end of file + diff --git a/CosNet.IDP/Properties/launchSettings.json b/CosNet.IDP/Properties/launchSettings.json index 41c66885..91615b23 100644 --- a/CosNet.IDP/Properties/launchSettings.json +++ b/CosNet.IDP/Properties/launchSettings.json @@ -4,7 +4,9 @@ "commandName": "Project", "launchBrowser": true, "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "COSNET_WEBUI_URL": "https://localhost:5001", + "COSNET_API_SECRET": "apisecret" }, "applicationUrl": "https://localhost:7001" } diff --git a/CosNet.IDP/Startup.cs b/CosNet.IDP/Startup.cs index 4f7b81e6..43a5d501 100644 --- a/CosNet.IDP/Startup.cs +++ b/CosNet.IDP/Startup.cs @@ -49,6 +49,7 @@ public void ConfigureServices(IServiceCollection services) }) .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) + .AddInMemoryApiResources(Config.ApiResources) .AddInMemoryClients(Config.Clients) .AddAspNetIdentity(); @@ -59,7 +60,7 @@ public void ConfigureServices(IServiceCollection services) .AddGoogle(options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; - + // register your IdentityServer with Google at https://console.developers.google.com // enable the Google+ API // set the redirect URI to https://localhost:5001/signin-google @@ -79,12 +80,15 @@ public void Configure(IApplicationBuilder app) app.UseStaticFiles(); app.UseRouting(); + app.UseIdentityServer(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } } -} \ No newline at end of file +} diff --git a/CosNet.WebUI/CosNet.WebUI.csproj b/CosNet.WebUI/CosNet.WebUI.csproj index bf15f228..1cf83859 100644 --- a/CosNet.WebUI/CosNet.WebUI.csproj +++ b/CosNet.WebUI/CosNet.WebUI.csproj @@ -6,13 +6,14 @@ service-worker-assets.js - - - - - - - + + + + + + + + @@ -21,4 +22,4 @@ - \ No newline at end of file + diff --git a/CosNet.WebUI/Pages/FetchData.razor b/CosNet.WebUI/Pages/FetchData.razor index 1d373b39..7c05da7d 100644 --- a/CosNet.WebUI/Pages/FetchData.razor +++ b/CosNet.WebUI/Pages/FetchData.razor @@ -1,6 +1,7 @@ @page "/fetchdata" @inject HttpClient Http @inject IConfiguration Configuration +@attribute [Authorize]

Weather forecast

@@ -8,49 +9,49 @@ @if (forecasts == null) { -

Loading...

+

Loading...

} else { - - +
+ + + + + + + + + + @foreach (var forecast in forecasts) + { - - - - + + + + - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
DateTemp. (C)Temp. (F)Summary@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
+ } + + } @code { - private WeatherForecast[] forecasts; + private WeatherForecast[] forecasts; - protected override async Task OnInitializedAsync() - { - forecasts = await Http.GetFromJsonAsync($"{Configuration.GetSection("CosNetAPIUrl").Value}/weatherforecast"); - } + protected override async Task OnInitializedAsync() + { + forecasts = await Http.GetFromJsonAsync($"{Configuration.GetSection("CosNetAPIUrl").Value}/weatherforecast"); + } - public class WeatherForecast - { - public DateTime Date { get; set; } + public class WeatherForecast + { + public DateTime Date { get; set; } - public int TemperatureC { get; set; } + public int TemperatureC { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - public string Summary { get; set; } - } + public string Summary { get; set; } + } } diff --git a/CosNet.WebUI/Program.cs b/CosNet.WebUI/Program.cs index b4ed0866..a701f331 100644 --- a/CosNet.WebUI/Program.cs +++ b/CosNet.WebUI/Program.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.AspNetCore.Components.WebAssembly.Authentication; namespace CosNet.WebUI { @@ -18,13 +19,24 @@ public static async Task Main(string[] args) var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("app"); - builder.Services.AddScoped(sp => new HttpClient()); + builder.Services.AddHttpClient("api") + .AddHttpMessageHandler(sp => + { + var handler = sp.GetService() + .ConfigureHandler( + authorizedUrls: new[] { builder.Configuration["CosNetAPIUrl"] }, + scopes: new[] { "cosnet-api" }); + + return handler; + }); + + builder.Services.AddScoped(sp => sp.GetService().CreateClient("api")); builder.Services.AddOidcAuthentication(options => { // Configure your authentication provider options here. // For more information, see https://aka.ms/blazor-standalone-auth - builder.Configuration.Bind("Local", options.ProviderOptions); + builder.Configuration.Bind("oidc", options.ProviderOptions); }); await builder.Build().RunAsync(); diff --git a/CosNet.WebUI/_Imports.razor b/CosNet.WebUI/_Imports.razor index 82794c53..3e111782 100644 --- a/CosNet.WebUI/_Imports.razor +++ b/CosNet.WebUI/_Imports.razor @@ -1,5 +1,6 @@ @using System.Net.Http @using System.Net.Http.Json +@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing diff --git a/CosNet.WebUI/wwwroot/appsettings.Development.json b/CosNet.WebUI/wwwroot/appsettings.Development.json index b9c2bf03..ae415e12 100644 --- a/CosNet.WebUI/wwwroot/appsettings.Development.json +++ b/CosNet.WebUI/wwwroot/appsettings.Development.json @@ -1,9 +1,12 @@ { - "Local": { - "Authority": "https:login.microsoftonline.com/", - "ClientId": "33333333-3333-3333-33333333333333333" - }, - "CosNetAPIUrl": "https://localhost:6001", - "CosNetIDPUrl": "https://localhost:7001" + "CosNetIDPUrl": "https://localhost:7001", + + "oidc": { + "Authority": "https://localhost:7001/", + "ClientId": "cosnetwebui", + "DefaultScopes": ["openid", "profile", "cosnet-api"], + "PostLogoutRedirectUri": "https://localhost:5001/", + "ResponseType": "code" + } } diff --git a/CosNet.WebUI/wwwroot/appsettings.json b/CosNet.WebUI/wwwroot/appsettings.json index 07ff3996..d7369d34 100644 --- a/CosNet.WebUI/wwwroot/appsettings.json +++ b/CosNet.WebUI/wwwroot/appsettings.json @@ -1,9 +1,12 @@ { - "Local": { - "Authority": "https:login.microsoftonline.com/", - "ClientId": "33333333-3333-3333-33333333333333333" - }, - "CosNetAPIUrl": "https://cosnet-api.azurewebsites.net", - "CosNetIDPUrl": "https://cosnet-idp.azurewebsites.net" + "CosNetIDPUrl": "https://cosnet-idp.azurewebsites.net", + + "oidc": { + "Authority": "https://cosnet-idp.azurewebsites.net", + "ClientId": "cosnetwebui", + "DefaultScopes": ["openid", "profile", "cosnet-api"], + "PostLogoutRedirectUri": "https://cosnet.azurewebsites.net", + "ResponseType": "code" + } }