Skip to content

Instantly share code, notes, and snippets.

@oledid
Last active April 3, 2020 08:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oledid/5541651039e67f5bd8e254397709e2ab to your computer and use it in GitHub Desktop.
Save oledid/5541651039e67f5bd8e254397709e2ab to your computer and use it in GitHub Desktop.
OpenIdConnect against ID-porten, an example for ASP .NET Core 3.1
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