/// <summary> /// Method that will get the Microsoft Graph token. /// </summary> /// <param name="graphConfigurationDetails">The graph configuration details.</param> /// <returns>The string that represents the Microsoft Graph token.</returns> private async Task <string> GetAccessTokenAsync(GraphConfigurationDetails graphConfigurationDetails) { string authority = $"{graphConfigurationDetails.Instance}{graphConfigurationDetails.TenantId}"; var cache = new RedisTokenCache(this.cache, graphConfigurationDetails.ClientId); var authContext = new AuthenticationContext(authority, cache); var userIdentity = new UserIdentifier(graphConfigurationDetails.ShiftsAdminAadObjectId, UserIdentifierType.UniqueId); try { var result = await authContext.AcquireTokenSilentAsync( "https://graph.microsoft.com", new ClientCredential( graphConfigurationDetails.ClientId, graphConfigurationDetails.ClientSecret), userIdentity).ConfigureAwait(false); return(result.AccessToken); } catch (AdalException adalEx) { this.telemetryClient.TrackException(adalEx); var retryResult = await authContext.AcquireTokenAsync( "https://graph.microsoft.com", new ClientCredential( graphConfigurationDetails.ClientId, graphConfigurationDetails.ClientSecret)).ConfigureAwait(false); return(retryResult.AccessToken); } }
/// <summary> /// Method that will obtain the Graph token. /// </summary> /// <param name="tenantId">The Tenant ID.</param> /// <param name="instance">The instance.</param> /// <param name="clientId">The App ID of the Web Application.</param> /// <param name="clientSecret">The client secret.</param> /// <param name="userId">The AAD Object ID of the Admin, could also be the UPN.</param> /// <returns>A string that represents the Microsoft Graph API token.</returns> public async Task <string> GetAccessTokenAsync( string tenantId, string instance, string clientId, string clientSecret, string userId = default(string)) { string authority = $"{instance}{tenantId}"; var cache = new RedisTokenCache(this.cache, clientId); var authContext = new AuthenticationContext(authority, cache); var userIdentity = new UserIdentifier(userId, UserIdentifierType.UniqueId); try { var result = await authContext.AcquireTokenSilentAsync( "https://graph.microsoft.com", new ClientCredential( clientId, clientSecret), userIdentity).ConfigureAwait(false); return(result.AccessToken); } catch (AdalException adalEx) { this.telemetryClient.TrackException(adalEx); var retryResult = await authContext.AcquireTokenAsync( "https://graph.microsoft.com", new ClientCredential( clientId, clientSecret)).ConfigureAwait(false); return(retryResult.AccessToken); } }
/// <summary> /// This method gets called by the runtime. Use this method to add services to the container. /// </summary> /// <param name="services">The service collection.</param> public void ConfigureServices(IServiceCollection services) { services.AddMvc(opts => { opts.Filters.Add(typeof(AdalTokenAcquisitionExceptionFilterAttribute)); }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddDataProtection(); // Wiring up App Insights services.AddApplicationInsightsTelemetry(); services.AddSingleton <IKeyVaultHelper, KeyVaultHelper>(); services.AddSingleton <AppSettings>(); services.AddSingleton <IApiHelper, ApiHelper>(); var serviceProvider = services.BuildServiceProvider(); var appSettings = serviceProvider.GetService <AppSettings>(); // Add a strongly-typed options class to DI services.Configure <AuthOptionsModel>(opt => { opt.Authority = appSettings.Authority; opt.ClientId = appSettings.ClientId; opt.ClientSecret = appSettings.ClientSecret; }); services.AddScoped <ITokenCacheFactory, TokenCacheFactory>(); services.AddStackExchangeRedisCache(options => { options.Configuration = appSettings.RedisCacheConfiguration; options.InstanceName = this.Configuration["RedisCacheInstanceName"]; }); services.AddHttpClient("ShiftsKronosIntegrationAPI", c => { c.BaseAddress = new Uri(this.Configuration["BaseAddressFirstTimeSync"]); c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); }).AddPolicyHandler(GetRetryPolicy()); services.AddHttpClient("GraphBetaAPI", client => { client.BaseAddress = new Uri(this.Configuration["GraphApiUrl"]); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); }).AddPolicyHandler(GetRetryPolicy()); services.AddAuthentication(auth => { auth.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; auth.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; auth.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(opts => { opts.SlidingExpiration = true; opts.AccessDeniedPath = new PathString("/Account/AccessDenied"); }) .AddOpenIdConnect( opts => { #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously opts.Events.OnTicketReceived = async(context) => #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { context.Properties.ExpiresUtc = DateTime.UtcNow.AddHours(1); }; opts.ClientId = this.Configuration["ClientId"]; opts.ClientSecret = this.Configuration["ClientSecret"]; opts.Authority = this.Configuration["Authority"]; opts.ResponseType = this.Configuration["ResponseType"]; this.Configuration.Bind(opts); opts.TokenValidationParameters.ValidateIssuer = false; opts.Events = new OpenIdConnectEvents { OnAuthorizationCodeReceived = async ctx => { HttpRequest request = ctx.HttpContext.Request; // We need to also specify the redirect URL used string currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path); // Credentials for app itself var credential = new ClientCredential(appSettings.ClientId, appSettings.ClientSecret); // Construct token cache var distributedCache = ctx.HttpContext.RequestServices.GetRequiredService <IDistributedCache>(); var cache = new RedisTokenCache(distributedCache, appSettings.ClientId); var authContext = new AuthenticationContext("https://login.microsoftonline.com/common", cache); // Get token for Microsoft Graph API using the authorization code string resource = "https://graph.microsoft.com"; AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync( ctx.ProtocolMessage.Code, new Uri(currentUri), credential, resource).ConfigureAwait(false); // Tell the OIDC middleware we got the tokens, it doesn't need to do anything ctx.HandleCodeRedemption(result.AccessToken, result.IdToken); }, }; }); 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.AddOptions(); services.AddHttpClient(); // As each sub-integration is being implemented, we need to make sure that we can correctly setup the DI.g services.AddSingleton <BusinessLogic.Providers.IConfigurationProvider>((provider) => new BusinessLogic.Providers.ConfigurationProvider( appSettings.StorageConnectionString, provider.GetRequiredService <TelemetryClient>())); services.AddSingleton <ITeamDepartmentMappingProvider>((provider) => new TeamDepartmentMappingProvider( appSettings.StorageConnectionString, provider.GetRequiredService <TelemetryClient>())); services.AddSingleton <IUserMappingProvider>((provider) => new UserMappingProvider( appSettings.StorageConnectionString, provider.GetRequiredService <TelemetryClient>())); services.AddSingleton <IGraphUtility>((provider) => new GraphUtility( provider.GetRequiredService <TelemetryClient>(), provider.GetRequiredService <IDistributedCache>(), provider.GetRequiredService <System.Net.Http.IHttpClientFactory>())); services.AddSingleton <ShiftsTeamKronosDepartmentViewModel>(); services.AddSingleton <IShiftMappingEntityProvider>((provider) => new ShiftMappingEntityProvider( provider.GetRequiredService <TelemetryClient>(), appSettings.StorageConnectionString)); services.AddSingleton <IOpenShiftMappingEntityProvider>((provider) => new OpenShiftMappingEntityProvider( provider.GetRequiredService <TelemetryClient>(), appSettings.StorageConnectionString)); services.AddSingleton <ITimeOffMappingEntityProvider>((provider) => new TimeOffMappingEntityProvider( provider.GetRequiredService <TelemetryClient>(), appSettings.StorageConnectionString)); services.AddSingleton <IAzureTableStorageHelper>((provider) => new AzureTableStorageHelper( appSettings.StorageConnectionString, provider.GetRequiredService <TelemetryClient>())); services.AddSingleton((provider) => new Utility( provider.GetRequiredService <TelemetryClient>(), provider.GetRequiredService <ILogonActivity>(), provider.GetRequiredService <AppSettings>(), provider.GetRequiredService <IDistributedCache>(), provider.GetRequiredService <BusinessLogic.Providers.IConfigurationProvider>(), provider.GetRequiredService <IAzureTableStorageHelper>(), provider.GetRequiredService <IGraphUtility>())); services.AddSingleton((provider) => new TeamDepartmentMappingController( provider.GetRequiredService <ITeamDepartmentMappingProvider>(), provider.GetRequiredService <TelemetryClient>(), provider.GetRequiredService <ILogonActivity>(), provider.GetRequiredService <IHyperFindLoadAllActivity>(), provider.GetRequiredService <ShiftsTeamKronosDepartmentViewModel>(), provider.GetRequiredService <Utility>(), provider.GetRequiredService <IGraphUtility>(), provider.GetRequiredService <AppSettings>(), provider.GetRequiredService <BusinessLogic.Providers.IConfigurationProvider>(), provider.GetRequiredService <IDistributedCache>(), provider.GetRequiredService <IUserMappingProvider>(), provider.GetRequiredService <System.Net.Http.IHttpClientFactory>())); services.AddSingleton((provider) => new UserMappingController( provider.GetRequiredService <AppSettings>(), provider.GetRequiredService <IGraphUtility>(), provider.GetRequiredService <ILogonActivity>(), provider.GetRequiredService <IHyperFindActivity>(), provider.GetRequiredService <TelemetryClient>(), provider.GetRequiredService <IUserMappingProvider>(), provider.GetRequiredService <ITeamDepartmentMappingProvider>(), provider.GetRequiredService <BusinessLogic.Providers.IConfigurationProvider>(), provider.GetRequiredService <IJobAssignmentActivity>(), provider.GetRequiredService <IHostingEnvironment>(), provider.GetRequiredService <Utility>())); // Wiring up Kronos dependency chain to set up DI container. services.AddSingleton <App.KronosWfc.Models.RequestEntities.Logon.Request>(); services.AddSingleton <ILogonActivity, LogonActivity>((provider) => new LogonActivity( new App.KronosWfc.Models.RequestEntities.Logon.Request(), provider.GetRequiredService <TelemetryClient>(), provider.GetRequiredService <IApiHelper>())); services.AddSingleton <IHyperFindLoadAllActivity, HyperFindLoadAllActivity>((provider) => new HyperFindLoadAllActivity( provider.GetRequiredService <TelemetryClient>(), provider.GetRequiredService <IApiHelper>())); services.AddSingleton <IHyperFindActivity, HyperFindActivity>((provider) => new HyperFindActivity( provider.GetRequiredService <TelemetryClient>(), provider.GetRequiredService <IApiHelper>())); services.AddSingleton <IJobAssignmentActivity, JobAssignmentActivity>((provider) => new JobAssignmentActivity( provider.GetRequiredService <TelemetryClient>(), provider.GetRequiredService <IApiHelper>())); }