Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Linq;
- using System.Security.Claims;
- using System.Threading.Tasks;
- using System.Web.Http;
- using System.Web.Http.Dispatcher;
- using Microsoft.ApplicationInsights.Extensibility;
- using Microsoft.Azure.ActiveDirectory.GraphClient;
- using Microsoft.IdentityModel.Clients.ActiveDirectory;
- using Microsoft.Owin;
- using Microsoft.Owin.Security;
- using Microsoft.Owin.Security.Cookies;
- using Microsoft.Owin.Security.OpenIdConnect;
- using Microsoft.Practices.Unity;
- using MSCorp.Immersion.Api.Infrastructure.Authentication;
- using MSCorp.Immersion.Api.Infrastructure.Authorization;
- using MSCorp.Immersion.Api.Infrastructure.Configuration;
- using MSCorp.Immersion.Api.Infrastructure.Logging;
- using MSCorp.Immersion.Api.Models;
- using MSCorp.Immersion.Api.Services.Interfaces;
- using MSCorp.Immersion.Data;
- using Newtonsoft.Json;
- using Owin;
- using Thinktecture.IdentityModel.Owin;
- using AuthenticationTypes = MSCorp.Immersion.Api.Infrastructure.AuthenticationTypes;
- using static MSCorp.Immersion.Api.Infrastructure.Logging.ApiEventSource;
- [assembly: OwinStartup(typeof(MSCorp.Immersion.Api.Startup))]
- namespace MSCorp.Immersion.Api
- {
- public partial class Startup
- {
- public void Configuration(IAppBuilder app)
- {
- ConfigureLogging();
- ConfigureDatabase();
- ConfigureAuth(app);
- // The public-facing V1 Api
- app.Map("/api/v1", ConfigureV1Api);
- // Api which serves the website only
- app.Map("/api/internal", ConfigureInternalApi);
- }
- private static void ConfigureLogging()
- {
- TelemetryConfiguration.Active.SetInstrumentationKeyFromConfig();
- LoggingConfiguration.ConfigureEventTracing();
- }
- private static void ConfigureDatabase()
- {
- DatabaseInitializer.SetDefault();
- }
- private static void ConfigureV1Api(IAppBuilder app)
- {
- // NOTE(benf): Create as child container to allow api-only container customization
- var container = UnityConfiguration.Container.CreateChildContainer();
- container.RegisterInstance<IHttpControllerTypeResolver>(
- new DefaultHttpControllerTypeResolver(
- predicate: t => t.Namespace?.StartsWith("MSCorp.Immersion.Api.Controllers.Api.V1") ?? false));
- AddApiKeyAuth(app, StaticConfiguration.AuthenticationApiKey, StaticConfiguration.AuthenticationApiSecret, Roles.ApiRole);
- AddApiKeyAuth(app, StaticConfiguration.AuthenticationTelemetryKey, StaticConfiguration.AuthenticationTelemetrySecret, Roles.TelemetryRole);
- var config = new HttpConfiguration();
- config.EnableApplicationInsights();
- config.MapHttpAttributeRoutes();
- config.ConfigureSsl();
- config.ConfigureFormatting();
- config.ConfigureErrorHandling();
- config.DependencyResolver = new UnityResolver(container);
- config.Filters.Add(new AuthorizeAttribute());
- app.UseWebApi(config);
- // NOTE(benf): ensure that all requests to the /api/v1 endpoint are handled in the event of WebAPI failing to map the request
- UseHandlerNotFound(app);
- }
- private static void AddApiKeyAuth(IAppBuilder app, string apiKey, string apiSecret, string apiRole)
- {
- app.UseBasicAuthentication(new BasicAuthenticationOptions("Web", (key, secret) =>
- Task.FromResult(string.Equals(apiKey, key, StringComparison.Ordinal) &&
- string.Equals(apiSecret, secret, StringComparison.Ordinal)
- ? new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, key), new Claim(ClaimsIdentity.DefaultRoleClaimType, apiRole) }.AsEnumerable()
- : null)));
- }
- private static void ConfigureInternalApi(IAppBuilder app)
- {
- // NOTE(benf): Create as child container to allow api-only container customization
- var container = UnityConfiguration.Container.CreateChildContainer();
- container.RegisterInstance<IHttpControllerTypeResolver>(
- new DefaultHttpControllerTypeResolver(
- predicate: t => t.Namespace?.StartsWith("MSCorp.Immersion.Api.Controllers.Api.Internal") ?? false));
- var config = new HttpConfiguration();
- config.EnableApplicationInsights();
- config.MapHttpAttributeRoutes();
- config.ConfigureSsl();
- config.ConfigureFormatting();
- config.ConfigureWebApiThrottle();
- config.ConfigureErrorHandling();
- config.DependencyResolver = new UnityResolver(container);
- config.Filters.Add(new AuthorizeAttribute());
- app.UseWebApi(config);
- // NOTE(benf): ensure that all requests to the /api/internal endpoint are handled in the event of WebAPI failing to map the request
- UseHandlerNotFound(app);
- }
- private static void ConfigureAuth(IAppBuilder app)
- {
- app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
- app.UseCookieAuthentication(new CookieAuthenticationOptions());
- // MICROSOFT EMPLOYEE AUTHENTICATION
- app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions(AuthenticationTypes.Aad)
- {
- ClientId = StaticConfiguration.AuthenticationAadClientId,
- Authority = $"https://login.microsoftonline.com/{StaticConfiguration.AuthenticationAadTenantId}",
- PostLogoutRedirectUri = StaticConfiguration.AuthenticationPostLogoutRedirectUri,
- Notifications = new OpenIdConnectAuthenticationNotifications
- {
- AuthorizationCodeReceived = async context =>
- {
- await InitializeUserEmailAsync(context.AuthenticationTicket.Identity, new Uri(StaticConfiguration.AuthenticationPostLogoutRedirectUri), context.Code);
- await InitializeUserAsync(context.AuthenticationTicket.Identity, false);
- },
- RedirectToIdentityProvider = context =>
- {
- context.ProtocolMessage.DomainHint = StaticConfiguration.AuthenticationAadDomainHint;
- return Task.FromResult(0);
- },
- SecurityTokenValidated = context =>
- {
- Log.AadSignInComplete(context.AuthenticationTicket.Identity.Name);
- context.AuthenticationTicket.Identity.AddClaim(new Claim(Claims.AuthenticationType, AuthenticationTypes.Aad));
- context.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, Roles.PartnerRole));
- context.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, Roles.EmployeeRole));
- context.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, Roles.RegisteredRole));
- return Task.FromResult(0);
- },
- AuthenticationFailed = context =>
- {
- Log.AadSignInFailed(context.Exception);
- context.HandleResponse();
- context.Response.Redirect($"{StaticConfiguration.BaseSiteUrl}/Account/Unauthorized");
- return Task.FromResult(0);
- }
- }
- });
- //#if DEBUG
- // // STUB USER AUTHENTICATION
- // app.Use(typeof(FakeAuthenticationMiddleware), new FakeAuthenticationOptions(AuthenticationTypes.Msa)
- // {
- // Path = "/account/signin/customer",
- // Claims = new[] {
- // new Claim(ClaimsIdentity.DefaultNameClaimType, "00037FFEB573ED28"),
- // new Claim(ClaimTypes.GivenName, "James"),
- // new Claim(ClaimTypes.Surname, "Customer"),
- // new Claim(Claims.AuthenticationType, AuthenticationTypes.Msa),
- // new Claim(ClaimTypes.Email, "a@b.com"),
- // new Claim(Claims.ClientId, "i"),
- // new Claim(ClaimTypes.Role, Roles.RegisteredRole)
- // }
- // });
- // app.Use(typeof(FakeAuthenticationMiddleware), new FakeAuthenticationOptions(AuthenticationTypes.Msa)
- // {
- // Path = "/account/signin/partner",
- // Claims = new[] {
- // new Claim(ClaimsIdentity.DefaultNameClaimType, "00037FFEB573ED29"),
- // new Claim(ClaimTypes.GivenName, "James"),
- // new Claim(ClaimTypes.Surname, "Partner"),
- // new Claim(Claims.AuthenticationType, AuthenticationTypes.Msa),
- // new Claim(ClaimTypes.Email, "a@b.com"),
- // new Claim(Claims.ClientId, "i"),
- // new Claim(ClaimTypes.Role, Roles.RegisteredRole),
- // new Claim(ClaimTypes.Role, Roles.PartnerRole),
- // new Claim(ClaimTypes.Role, Roles.ContentAdminRole),
- // new Claim(ClaimTypes.Role, Roles.BookingAdminRole),
- // new Claim(ClaimTypes.Role, Roles.VirtualClassAdminRole)
- // }
- // });
- // STUB USER AUTHENTICATION
- app.Use(typeof(FakeAuthenticationMiddleware), new FakeAuthenticationOptions(AuthenticationTypes.Msa)
- {
- Path = "/account/signin/customer",
- Claims = new[] {
- new Claim(ClaimsIdentity.DefaultNameClaimType, "000640008D4B2BC4"),
- new Claim(ClaimTypes.GivenName, "Jason"),
- new Claim(ClaimTypes.Surname, "Ciemiega"),
- new Claim(Claims.AuthenticationType, AuthenticationTypes.Msa),
- new Claim(ClaimTypes.Email, "jciemiega@hotmail.com"),
- new Claim(Claims.ClientId, "i"),
- new Claim(ClaimTypes.Role, Roles.RegisteredRole)
- }
- });
- app.Use(typeof(FakeAuthenticationMiddleware), new FakeAuthenticationOptions(AuthenticationTypes.Msa)
- {
- Path = "/account/signin/partner",
- Claims = new[] {
- new Claim(ClaimsIdentity.DefaultNameClaimType, "000640008D4B2BC4"),
- new Claim(ClaimTypes.GivenName, "Jason"),
- new Claim(ClaimTypes.Surname, "Ciemiega"),
- new Claim(Claims.AuthenticationType, AuthenticationTypes.Msa),
- new Claim(ClaimTypes.Email, "jciemiega@hotmail.com"),
- new Claim(Claims.ClientId, "i"),
- new Claim(ClaimTypes.Role, Roles.RegisteredRole),
- new Claim(ClaimTypes.Role, Roles.PartnerRole),
- new Claim(ClaimTypes.Role, Roles.ContentAdminRole),
- new Claim(ClaimTypes.Role, Roles.BookingAdminRole),
- new Claim(ClaimTypes.Role, Roles.VirtualClassAdminRole)
- }
- });
- //#else
- // REAL USER AUTHENTICATION
- //app.Use(typeof(RpsProxyAuthenticationMiddleware), new RpsProxyAuthenticationOptions(AuthenticationTypes.Msa)
- //{
- // AuthenticationMode = AuthenticationMode.Passive,
- // Proxy = StaticConfiguration.AuthenticationRpsProxyUri,
- // Key = StaticConfiguration.AuthenticationRpsKey,
- // SecurityTokenValidated = async context =>
- // {
- // var isPartnerRequest = context.AuthenticationTicket.Properties.Dictionary.Any(x => x.Key == "p" && x.Value == "1");
- // await InitializeUserAsync(context.AuthenticationTicket.Identity, isPartnerRequest);
- // await InitializeUserProfileAsync(context.AuthenticationTicket.Identity);
- // },
- // AuthenticationFailed = context =>
- // {
- // context.HandleResponse();
- // context.Response.Redirect($"{StaticConfiguration.BaseSiteUrl}/Account/Unauthorized");
- // return Task.FromResult(0);
- // }
- //});
- //#endif
- }
- private static async Task InitializeUserAsync(ClaimsIdentity identity, bool isPartnerRequest)
- {
- using (var container = UnityConfiguration.Container.CreateChildContainer())
- {
- var userService = container.Resolve<IUserService>();
- bool userExists = await userService.GetUserByExternalIdAsync(identity) != null;
- var user = await userService.AddOrUpdateUserAsync(identity);
- //Send Marketo Lead when a new user is created, but don't do for MS sign in.
- if (user.AuthType != AuthenticationTypes.Aad && !userExists)
- {
- await InitializeMarketoLead(identity);
- }
- if (user.Blocked.GetValueOrDefault())
- {
- throw new Exception($"User is blocked: {identity.Name}");
- }
- // TODO(benf): Run this on first sign-in only?
- var sellerBookRepository = container.Resolve<ISellerBookService>();
- // Link this user with any unlinked "facilitator" records in their name (email)
- await sellerBookRepository.UpdateSellerBookFacilitatorsUserId(user.Email, user.Id);
- // If the user is a Microsoft Employee, also search on their External ID
- // NOTE(benf): ExternalId for employees is their Microsoft alias. Email will be friendly alias
- // e.g. Email = Joe.Bloggs@microsoft.com
- // ExternalId = jbloggs@microsoft.com
- if (user.AuthType == AuthenticationTypes.Aad)
- {
- await sellerBookRepository.UpdateSellerBookFacilitatorsUserId(user.ExternalId, user.Id);
- }
- var authService = container.Resolve<IAuthorizationService>();
- var claims = await authService.GetUserClaimsAsync(user.Id);
- if (isPartnerRequest && StaticConfiguration.AuthorizationPvpEnabled)
- {
- var partnerClaims = await authService.GetUserPartnerClaimsAsync(user.Id);
- claims = claims.Concat(partnerClaims).ToList();
- }
- claims.Add(new Claim(Claims.UserId, user.Id.ToString()));
- // If the partner status is assigned by the database roles list, then only apply it when
- // the user is logging in as a partner
- foreach (var role in claims.Where(x => isPartnerRequest || !string.Equals(x.Value, Roles.PartnerRole, StringComparison.OrdinalIgnoreCase)))
- {
- identity.AddClaim(role);
- }
- }
- }
- private static async Task InitializeUserProfileAsync(ClaimsIdentity identity)
- {
- using (var container = UnityConfiguration.Container.CreateChildContainer())
- {
- var userService = container.Resolve<IUserService>();
- var profileService = container.Resolve<IProfileRegistrationService>();
- var puid = identity.Claims.Single(x => x.Type == ClaimsIdentity.DefaultNameClaimType).Value;
- var profile = await profileService.FindProfileAsync(new ProfilePuid {Puid = puid});
- if (profile != null)
- {
- var userIdClaim = identity.Claims.Single(x => x.Type == Claims.UserId);
- await userService.UpdateUserDetailsAsync(new Guid(userIdClaim.Value), profile.Country, profile.PreferredEmail);
- identity.AddClaim(new Claim(ClaimTypes.Role, Roles.RegisteredRole));
- }
- }
- }
- private static async Task InitializeMarketoLead(ClaimsIdentity identity)
- {
- try
- {
- using (var container = UnityConfiguration.Container.CreateChildContainer())
- {
- var profileService = container.Resolve<IProfileRegistrationService>();
- var puid = identity.Claims.Single(x => x.Type == ClaimsIdentity.DefaultNameClaimType).Value;
- await profileService.SendMarketoLeadForNewProfileAsync(new ProfilePuid { Puid = puid });
- }
- }
- catch (Exception ex)
- {
- Log.MarketoIntializeFailed(ex);
- }
- }
- private static async Task InitializeUserEmailAsync(ClaimsIdentity identity, Uri redirectUri, string code)
- {
- var client = new ActiveDirectoryClient(new Uri($"https://graph.windows.net/{StaticConfiguration.AuthenticationAadTenantId}"),
- async () =>
- {
- var credential = new ClientCredential(StaticConfiguration.AuthenticationAadClientId, StaticConfiguration.AuthenticationAadClientKey);
- var authContext = new AuthenticationContext($"https://login.microsoftonline.com/{StaticConfiguration.AuthenticationAadTenantId}", null);
- var result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, redirectUri, credential, "https://graph.windows.net");
- return result.AccessToken;
- });
- var me = await client.Me.ExecuteAsync();
- if (!string.IsNullOrEmpty(me.Mail))
- {
- identity.AddClaim(new Claim(ClaimTypes.Email, me.Mail));
- }
- }
- private static void UseHandlerNotFound(IAppBuilder app)
- {
- app.Use((ctx, next) =>
- {
- ctx.Response.StatusCode = 404;
- ctx.Response.Headers.Set("Content-Type", "application/json");
- ctx.Response.Write(JsonConvert.SerializeObject(new { message = "Handler not found" }));
- return Task.FromResult(0);
- });
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement