Last active
April 3, 2020 08:47
-
-
Save oledid/5541651039e67f5bd8e254397709e2ab to your computer and use it in GitHub Desktop.
OpenIdConnect against ID-porten, an example for ASP .NET Core 3.1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Security.Claims; | |
using System.Threading.Tasks; | |
using Microsoft.AspNetCore.Authentication; | |
using Microsoft.AspNetCore.Authorization; | |
using Microsoft.AspNetCore.Builder; | |
using Microsoft.AspNetCore.Diagnostics; | |
using Microsoft.AspNetCore.Hosting; | |
using Microsoft.AspNetCore.Http; | |
using Microsoft.AspNetCore.HttpsPolicy; | |
using Microsoft.Extensions.Configuration; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Hosting; | |
using Microsoft.IdentityModel.Protocols; | |
using Microsoft.IdentityModel.Protocols.OpenIdConnect; | |
using Microsoft.IdentityModel.Tokens; | |
using Newtonsoft.Json; | |
using Newtonsoft.Json.Serialization; | |
namespace Web | |
{ | |
public static class AppAuthenticationSchemes | |
{ | |
public const string AppCookies = nameof(AppCookies); | |
public const string IdPorten = nameof(IdPorten); | |
} | |
public static class AppClaimTypes | |
{ | |
public const string Name = ClaimsIdentity.DefaultNameClaimType; | |
public const string Fnr = "pid"; | |
public const string IdToken = "id-token"; | |
} | |
interface IPersonRepository | |
{ | |
Task CreatePersonIfNotExists(string fnrString); | |
} | |
public class Startup | |
{ | |
private readonly IConfiguration configuration; | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.Configure<CookiePolicyOptions>(options => | |
{ | |
options.MinimumSameSitePolicy = SameSiteMode.None; | |
}); | |
services.AddAuthentication(sharedOptions => | |
{ | |
sharedOptions.DefaultAuthenticateScheme = AppAuthenticationSchemes.AppCookies; | |
sharedOptions.DefaultScheme = AppAuthenticationSchemes.AppCookies; | |
sharedOptions.DefaultChallengeScheme = AppAuthenticationSchemes.AppCookies; | |
}) | |
.AddCookie(AppAuthenticationSchemes.AppCookies, options => | |
{ | |
options.AccessDeniedPath = "/AccessDenied"; | |
options.LoginPath = "/"; | |
}) | |
.AddOpenIdConnect(AppAuthenticationSchemes.IdPorten, options => | |
{ | |
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(configuration["Secrets:IdPorten:WellKnown"], new OpenIdConnectConfigurationRetriever()); | |
options.Configuration = configurationManager.GetConfigurationAsync().Result; | |
options.ClientId = configuration["Secrets:IdPorten:ClientId"]; | |
options.ClientSecret = configuration["Secrets:IdPorten:ClientSecret"]; | |
options.CallbackPath = "/signin-oidc"; // this GET-method will be auto-generated by AddOpenIdConnect | |
options.SignedOutCallbackPath = "/signout-oidc"; // this GET-method will be auto-generated by AddOpenIdConnect | |
options.RemoteSignOutPath = "/frontchannelLogout-oidc"; // this GET-method will be auto-generated by AddOpenIdConnect | |
options.SignedOutRedirectUri = "/LoggedOut"; // not auto-generated, create this | |
options.ResponseType = "code"; | |
options.TokenValidationParameters = new TokenValidationParameters | |
{ | |
NameClaimType = AppClaimTypes.Name, | |
ValidateIssuerSigningKey = true, | |
IssuerSigningKeys = options.Configuration.SigningKeys | |
}; | |
options.Events.OnTokenValidated = async context => | |
{ | |
var identity = (ClaimsIdentity)context.Principal.Identity; | |
var fnrString = identity.FindFirst(AppClaimTypes.Fnr)?.Value; | |
// just as an example: | |
var personRepository = (IPersonRepository)context.HttpContext.RequestServices.GetService(typeof(IPersonRepository)); | |
await personRepository.CreatePersonIfNotExists(fnrString); | |
}; | |
options.Events.OnRedirectToIdentityProviderForSignOut = async context => | |
{ | |
var idToken = (context.HttpContext.User.Identity as ClaimsIdentity)?.Claims.FirstOrDefault(c => c.Type == AppClaimTypes.IdToken)?.Value; | |
if (idToken != null) | |
{ | |
// needed to sign out correctly | |
context.ProtocolMessage.IdTokenHint = idToken; | |
} | |
}; | |
options.Events.OnRemoteSignOut = async context => | |
{ | |
await context.HttpContext.SignOutAsync(AppAuthenticationSchemes.AppCookies); | |
}; | |
options.Events.OnAuthorizationCodeReceived = async context => | |
{ | |
var code = context.ProtocolMessage.Code; | |
// maybe you need the code for something? | |
}; | |
}); | |
services.AddAuthorization(options => | |
{ | |
// tip: read up on the differences between DefaultPolicy and FallbackPolicy | |
options.FallbackPolicy = new AuthorizationPolicyBuilder() | |
.RequireAuthenticatedUser() | |
.Build(); | |
}); | |
services.AddControllersWithViews() | |
.AddRazorRuntimeCompilation(); | |
services.AddRazorPages() | |
.AddRazorRuntimeCompilation(); | |
services.Configure<HstsOptions>(options => | |
{ | |
options.IncludeSubDomains = true; | |
options.MaxAge = TimeSpan.FromDays(365); | |
}); | |
JsonConvert.DefaultSettings = () => | |
{ | |
var settings = new JsonSerializerSettings(); | |
settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); | |
return settings; | |
}; | |
services.AddMemoryCache(); | |
services.AddSingleton<IPersonRepository, YourPersonRepository>(); | |
} | |
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | |
{ | |
app.UseExceptionHandler(errorApp => | |
{ | |
errorApp.Run(async context => | |
{ | |
var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); | |
var exception = exceptionHandlerPathFeature?.Error; | |
if (exception != null) | |
{ | |
try | |
{ | |
Console.WriteLine(exception.ToString()); // do something with the exception | |
} | |
catch | |
{ | |
// don't crash when logging crashes | |
} | |
} | |
}); | |
}); | |
app.UseHsts(); | |
app.UseHttpsRedirection(); | |
app.UseStaticFiles(); | |
app.UseRouting(); | |
app.UseCookiePolicy(); | |
app.UseAuthentication(); | |
app.UseAuthorization(); | |
app.UseStatusCodePages(); | |
app.UseEndpoints(endpoints => | |
{ | |
endpoints.MapDefaultControllerRoute(); | |
endpoints.MapRazorPages(); | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment