using System; using System.IO; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Google; using Microsoft.AspNetCore.Authentication.MicrosoftAccount; using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Authentication.Twitter; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Authentication; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Server.Kestrel.Filter; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; namespace SocialSample { /* Note all servers must use the same address and port because these are pre-registered with the various providers. */ public class Startup { public Startup() { Configuration = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("config.json") .AddUserSecrets() .Build(); } public IConfiguration Configuration { get; set; } public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(options => options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) { loggerfactory.AddConsole(LogLevel.Information); // Simple error page to avoid a repo dependency. app.Use(async (context, next) => { try { await next(); } catch (Exception ex) { if (context.Response.HasStarted) { throw; } context.Response.StatusCode = 500; await context.Response.WriteAsync(ex.ToString()); } }); app.UseCookieAuthentication(new CookieAuthenticationOptions { AutomaticAuthenticate = true, AutomaticChallenge = true, LoginPath = new PathString("/login") }); // You must first create an app with facebook and add it's ID and Secret to your config.json or user-secrets. // https://developers.facebook.com/apps/ app.UseFacebookAuthentication(new FacebookOptions { AppId = Configuration["facebook:appid"], AppSecret = Configuration["facebook:appsecret"], Scope = { "email" }, Fields = { "name", "email" }, SaveTokens = true, }); // See config.json app.UseOAuthAuthentication(new OAuthOptions { AuthenticationScheme = "Google-AccessToken", DisplayName = "Google-AccessToken", ClientId = Configuration["google:clientid"], ClientSecret = Configuration["google:clientsecret"], CallbackPath = new PathString("/signin-google-token"), AuthorizationEndpoint = GoogleDefaults.AuthorizationEndpoint, TokenEndpoint = GoogleDefaults.TokenEndpoint, Scope = { "openid", "profile", "email" }, SaveTokens = true }); // See config.json // https://console.developers.google.com/project app.UseGoogleAuthentication(new GoogleOptions { ClientId = Configuration["google:clientid"], ClientSecret = Configuration["google:clientsecret"], SaveTokens = true, Events = new OAuthEvents() { OnRemoteFailure = ctx => { ctx.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(ctx.Failure.Message)); ctx.HandleResponse(); return Task.FromResult(0); } } }); // See config.json // https://apps.twitter.com/ app.UseTwitterAuthentication(new TwitterOptions { ConsumerKey = Configuration["twitter:consumerkey"], ConsumerSecret = Configuration["twitter:consumersecret"], SaveTokens = true, Events = new TwitterEvents() { OnRemoteFailure = ctx => { ctx.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(ctx.Failure.Message)); ctx.HandleResponse(); return Task.FromResult(0); } } }); /* Azure AD app model v2 has restrictions that prevent the use of plain HTTP for redirect URLs. Therefore, to authenticate through microsoft accounts, tryout the sample using the following URL: https://localhost:54541/ */ // See config.json // https://apps.dev.microsoft.com/ app.UseOAuthAuthentication(new OAuthOptions { AuthenticationScheme = "Microsoft-AccessToken", DisplayName = "MicrosoftAccount-AccessToken", ClientId = Configuration["msa:clientid"], ClientSecret = Configuration["msa:clientsecret"], CallbackPath = new PathString("/signin-microsoft-token"), AuthorizationEndpoint = MicrosoftAccountDefaults.AuthorizationEndpoint, TokenEndpoint = MicrosoftAccountDefaults.TokenEndpoint, Scope = { "https://graph.microsoft.com/user.read" }, SaveTokens = true }); // See config.json // https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-app-registration/ app.UseMicrosoftAccountAuthentication(new MicrosoftAccountOptions { DisplayName = "MicrosoftAccount", ClientId = Configuration["msa:clientid"], ClientSecret = Configuration["msa:clientsecret"], SaveTokens = true }); // See config.json // https://github.com/settings/applications/ app.UseOAuthAuthentication(new OAuthOptions { AuthenticationScheme = "GitHub-AccessToken", DisplayName = "Github-AccessToken", ClientId = Configuration["github-token:clientid"], ClientSecret = Configuration["github-token:clientsecret"], CallbackPath = new PathString("/signin-github-token"), AuthorizationEndpoint = "https://github.com/login/oauth/authorize", TokenEndpoint = "https://github.com/login/oauth/access_token", SaveTokens = true }); // See config.json app.UseOAuthAuthentication(new OAuthOptions { AuthenticationScheme = "GitHub", DisplayName = "Github", ClientId = Configuration["github:clientid"], ClientSecret = Configuration["github:clientsecret"], CallbackPath = new PathString("/signin-github"), AuthorizationEndpoint = "https://github.com/login/oauth/authorize", TokenEndpoint = "https://github.com/login/oauth/access_token", UserInformationEndpoint = "https://api.github.com/user", ClaimsIssuer = "OAuth2-Github", SaveTokens = true, // Retrieving user information is unique to each provider. Events = new OAuthEvents { OnCreatingTicket = async context => { // Get the GitHub user var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted); response.EnsureSuccessStatusCode(); var user = JObject.Parse(await response.Content.ReadAsStringAsync()); var identifier = user.Value("id"); if (!string.IsNullOrEmpty(identifier)) { context.Identity.AddClaim(new Claim( ClaimTypes.NameIdentifier, identifier, ClaimValueTypes.String, context.Options.ClaimsIssuer)); } var userName = user.Value("login"); if (!string.IsNullOrEmpty(userName)) { context.Identity.AddClaim(new Claim( ClaimsIdentity.DefaultNameClaimType, userName, ClaimValueTypes.String, context.Options.ClaimsIssuer)); } var name = user.Value("name"); if (!string.IsNullOrEmpty(name)) { context.Identity.AddClaim(new Claim( "urn:github:name", name, ClaimValueTypes.String, context.Options.ClaimsIssuer)); } var link = user.Value("url"); if (!string.IsNullOrEmpty(link)) { context.Identity.AddClaim(new Claim( "urn:github:url", link, ClaimValueTypes.String, context.Options.ClaimsIssuer)); } } } }); // Choose an authentication type app.Map("/login", signoutApp => { signoutApp.Run(async context => { var authType = context.Request.Query["authscheme"]; if (!string.IsNullOrEmpty(authType)) { // By default the client will be redirect back to the URL that issued the challenge (/login?authtype=foo), // send them to the home page instead (/). await context.Authentication.ChallengeAsync(authType, new AuthenticationProperties() { RedirectUri = "/" }); return; } context.Response.ContentType = "text/html"; await context.Response.WriteAsync(""); await context.Response.WriteAsync("Choose an authentication scheme:
"); foreach (var type in context.Authentication.GetAuthenticationSchemes()) { await context.Response.WriteAsync("" + (type.DisplayName ?? "(suppressed)") + "
"); } await context.Response.WriteAsync(""); }); }); // Sign-out to remove the user cookie. app.Map("/logout", signoutApp => { signoutApp.Run(async context => { context.Response.ContentType = "text/html"; await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); await context.Response.WriteAsync(""); await context.Response.WriteAsync("You have been logged out. Goodbye " + context.User.Identity.Name + "
"); await context.Response.WriteAsync("Home"); await context.Response.WriteAsync(""); }); }); // Display the remote error app.Map("/error", errorApp => { errorApp.Run(async context => { context.Response.ContentType = "text/html"; await context.Response.WriteAsync(""); await context.Response.WriteAsync("An remote failure has occurred: " + context.Request.Query["FailureMessage"] + "
"); await context.Response.WriteAsync("Home"); await context.Response.WriteAsync(""); }); }); // Deny anonymous request beyond this point. app.Use(async (context, next) => { if (!context.User.Identities.Any(identity => identity.IsAuthenticated)) { // The cookie middleware will intercept this 401 and redirect to /login await context.Authentication.ChallengeAsync(); return; } await next(); }); // Display user information app.Run(async context => { context.Response.ContentType = "text/html"; await context.Response.WriteAsync(""); await context.Response.WriteAsync("Hello " + (context.User.Identity.Name ?? "anonymous") + "
"); foreach (var claim in context.User.Claims) { await context.Response.WriteAsync(claim.Type + ": " + claim.Value + "
"); } await context.Response.WriteAsync("Tokens:
"); await context.Response.WriteAsync("Access Token: " + await context.Authentication.GetTokenAsync("access_token") + "
"); await context.Response.WriteAsync("Refresh Token: " + await context.Authentication.GetTokenAsync("refresh_token") + "
"); await context.Response.WriteAsync("Token Type: " + await context.Authentication.GetTokenAsync("token_type") + "
"); await context.Response.WriteAsync("expires_at: " + await context.Authentication.GetTokenAsync("expires_at") + "
"); await context.Response.WriteAsync("Logout"); await context.Response.WriteAsync(""); }); } public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel(options => { //Configure SSL var serverCertificate = LoadCertificate(); options.UseHttps(serverCertificate); }) .UseIISIntegration() .UseStartup() .Build(); host.Run(); } private static X509Certificate2 LoadCertificate() { var socialSampleAssembly = typeof(Startup).GetTypeInfo().Assembly; var embeddedFileProvider = new EmbeddedFileProvider(socialSampleAssembly, "SocialSample"); var certificateFileInfo = embeddedFileProvider.GetFileInfo("compiler/resources/cert.pfx"); using (var certificateStream = certificateFileInfo.CreateReadStream()) { byte[] certificatePayload; using (var memoryStream = new MemoryStream()) { certificateStream.CopyTo(memoryStream); certificatePayload = memoryStream.ToArray(); } return new X509Certificate2(certificatePayload, "testPassword"); } } } }