using System; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Security.Claims; 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.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; 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(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json"); if (env.IsDevelopment()) { // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709 builder.AddUserSecrets(); } builder.AddEnvironmentVariables(); Configuration = builder.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"], // http://stackoverflow.com/questions/22627083/can-we-get-email-id-from-twitter-oauth-api/32852370#32852370 // http://stackoverflow.com/questions/36330675/get-users-email-from-twitter-api-for-external-login-authentication-asp-net-mvc?lq=1 RetrieveUserDetails = true, SaveTokens = true, Events = new TwitterEvents() { OnCreatingTicket = ctx => { var profilePic = ctx.User.Value("profile_image_url"); ctx.Principal.Identities.First().AddClaim(new Claim("urn:twitter:profilepicture", profilePic, ClaimTypes.Uri, ctx.Options.ClaimsIssuer)); return Task.FromResult(0); }, 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:44318/ */ // 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 email = user.Value("email"); if (!string.IsNullOrEmpty(email)) { context.Identity.AddClaim(new Claim( ClaimTypes.Email, email, ClaimValueTypes.Email, 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(""); }); }); app.Run(async context => { // CookieAuthenticationOptions.AutomaticAuthenticate = true (default) causes User to be set var user = context.User; // This is what [Authorize] calls // var user = await context.Authentication.AuthenticateAsync(AuthenticationManager.AutomaticScheme); // This is what [Authorize(ActiveAuthenticationSchemes = MicrosoftAccountDefaults.AuthenticationScheme)] calls // var user = await context.Authentication.AuthenticateAsync(MicrosoftAccountDefaults.AuthenticationScheme); // Deny anonymous request beyond this point. if (user == null || !user.Identities.Any(identity => identity.IsAuthenticated)) { // This is what [Authorize] calls // The cookie middleware will intercept this 401 and redirect to /login await context.Authentication.ChallengeAsync(); // This is what [Authorize(ActiveAuthenticationSchemes = MicrosoftAccountDefaults.AuthenticationScheme)] calls // await context.Authentication.ChallengeAsync(MicrosoftAccountDefaults.AuthenticationScheme); return; } // Display user information 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(""); }); } } }