Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time
258 lines (220 sloc) 11.4 KB
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using AdvantagePlatform.Data;
using AdvantagePlatform.Utility;
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Mappers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace AdvantagePlatform
public class Startup
public Startup(IConfiguration configuration)
Configuration = configuration;
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
services.Configure<CookiePolicyOptions>(options =>
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
services.AddDbContext<ApplicationDbContext>(options =>
// This is a sample app. Use simple passwords.
services.AddDefaultIdentity<AdvantagePlatformUser>(options =>
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredLength = 4;
// Use app specific cookie name so both AdvantagePlatform and AdvantageTool can run
// on localhost at the same time.
services.ConfigureApplicationCookie(options => { options.Cookie.Name = "AdvantagePlatform"; });
// Some pages require authorization.
.AddRazorPagesOptions(options => { options.Conventions.AuthorizeFolder("/CourseLinks"); })
.AddRazorPagesOptions(options => { options.Conventions.AuthorizeFolder("/PlatformLinks"); })
.AddRazorPagesOptions(options => { options.Conventions.AuthorizeFolder("/Tools"); })
// Add Swagger tools using Swashbuckle.AspNetCore
services.AddSwaggerGen(options =>
options.SwaggerDoc("v1", new Info
Title= "Advantage Platform",
Version = Assembly.GetEntryAssembly().GetName().Version.ToString(),
Description = "These are the LTI Advantage service endpoints implemented by this sample platform. "
+ "Click on Authorize and login with the username and password you registered to try the various services."
// This will include the xml-doc comments for each endpoint in the Swagger UI
options.IncludeXmlComments(Path.Combine(System.AppContext.BaseDirectory, "LtiAdvantage.xml"));
// Ruby controllers accept a format extension (i.e. ".json"). All the controllers
// in this sample platform have two routes for each endpoint, one with and one without
// the format extension. This filter will remove the route with the format extension.
options.SwaggerGeneratorOptions.DocumentFilters = new List<IDocumentFilter>
{new HideRubyRoutesInSwaggerFilter()};
// All the controllers in this sample platform require authorization. This
// SecurityDefinition will use validate the user is registered, then request
// an access token from the TokenUrl.
options.AddSecurityDefinition("oauth2", new OAuth2Scheme
TokenUrl = "/connect/token",
Type = "oauth2",
Flow = "password",
Scopes = Config.LtiScopes.ToDictionary(s => s, s => "")
// All the controllers in this sample require specific scopes. This
// SecurityRequirement will allow the user to select the scopes they
// want to test with.
options.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{ "oauth2", Config.LtiScopes }
// Add Identity Server configured to support LTI Advantage needs
services.AddIdentityServer(options =>
options.UserInteraction.LoginUrl = "/Identity/Account/Login";
options.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
// This is not appropriate for production
// Store Configuration and Operational data in the database
.AddConfigurationStore(options =>
options.ConfigureDbContext = builder =>
sql => sql.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name));
.AddOperationalStore(options =>
options.ConfigureDbContext = builder =>
sql => sql.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name));
// Clean up expired tokens
options.EnableTokenCleanup = true;
options.EnableTokenCleanup = false;
options.TokenCleanupInterval = 30;
// Configure IdentityServer to work with ASP.NET Core Identity
// Allow the application user to impersonate a Platform member (e.g. a student in the course)
// Custom profile service to add LTI Advantage claims to id_token
// Adds support for client authentication using JWT bearer assertions
// where token is signed by a private key stored in PEM format.
// Add authentication and set the default scheme to IdentityConstants.ApplicationScheme
// so that IdentityServer can find the right ASP.NET Core Identity pages
// Add Bearer authentication to authenticate API calls
.AddIdentityServerAuthentication(options =>
// The JwtBearer authentication handler will use the discovery endpoint
// of the authorization server to find the JWKS endpoint, to find the
// key to validate the JwtBearer token. Don't forget to define the
// base address of your Identity Server here. If you don't, you'll get
// "Unauthorized" errors when you call an API that is protected by a
// JwtBearer token.
options.Authority = Configuration["Authority"];
// Add LTI Advantage service authorization policies that enforce API scopes
// Each app user is a tenant with their own platform, course, and people. This
// course access validator is used by the controllers to make sure each tenant
// can only access their own course.
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
// Configure Identity Server
if (env.IsDevelopment())
// Fire up Identity Server (replaces app.UseAuthentication())
// Fire up Swagger and Swagger UI
app.UseSwaggerUI(options =>
options.SwaggerEndpoint("/swagger/v1/swagger.json", "LTI Advantage 1.3");
options.DocumentTitle = "Advantage Platform - Swagger UI";
/// <summary>
/// Configure Identity Server.
/// </summary>
private static void InitializeDatabase(IApplicationBuilder app)
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
if (!EnumerableExtensions.Any(context.Clients))
foreach (var client in Config.GetClients())
// Define the identity resources that can be requested.
if (!EnumerableExtensions.Any(context.IdentityResources))
foreach (var resource in Config.GetIdentityResources())
// Define the API's that will be protected.
if (!EnumerableExtensions.Any(context.ApiResources))
foreach (var resource in Config.GetApiResources())