public ValueTask <string> GetServiceAccessTokenAsync(CancellationToken cancellationToken) { var authenticationInformation = _authenticationInformationProvider.GetServiceAuthenticationInformation(); if (authenticationInformation.ServiceClientId == null || authenticationInformation.ServiceClientSecret == null) { throw new InvalidOperationException("Client credentials authentication information is not set for service."); } return(GetServiceAccessTokenAsync(new ClientCredentials( authenticationInformation.ServiceClientId, authenticationInformation.ServiceClientSecret, Enumerable.Empty <string>()), cancellationToken)); }
public async ValueTask <string> GetServiceAccessTokenAsync(CancellationToken cancellationToken) { var authenticationInformation = _authenticationInformationProvider.GetServiceAuthenticationInformation(); using var httpClient = new HttpClient(); using var content = new FormUrlEncodedContent(new[] { new KeyValuePair <string?, string?>("client_id", authenticationInformation.ServiceClientId), new KeyValuePair <string?, string?>("client_secret", authenticationInformation.ServiceClientSecret), new KeyValuePair <string?, string?>("audience", "https://api.typingrealm.com"), new KeyValuePair <string?, string?>("grant_type", "client_credentials") }); var response = await httpClient.PostAsync(authenticationInformation.TokenEndpoint, content) .ConfigureAwait(false); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync() .ConfigureAwait(false); var tokenData = JsonSerializer.Deserialize <TokenResponse>(json); if (tokenData?.access_token == null) { throw new InvalidOperationException("Access token is null, invalid conversion from token endpoint response."); } // TODO: Cache this token and renew only when expired / about to expire. return(tokenData.access_token); }
public ValueTask <AuthenticationResult> AuthenticateAsync(string accessToken, CancellationToken cancellationToken) { var authInfo = _authenticationInformationProvider.GetProfileAuthenticationInformation(); var result = TryValidateToken(accessToken, authInfo); if (result != null) { return(new ValueTask <AuthenticationResult>(result)); } foreach (var additionalAuthInfo in _authenticationInformationProvider.GetAdditionalProfileAuthenticationInformations()) { result = TryValidateToken(accessToken, additionalAuthInfo); if (result != null) { return(new ValueTask <AuthenticationResult>(result)); } } var serviceAuthInfo = _authenticationInformationProvider.GetServiceAuthenticationInformation(); result = TryValidateToken(accessToken, serviceAuthInfo); if (result != null) { return(new ValueTask <AuthenticationResult>(result)); } throw new NotSupportedException("Security token is not a valid JWT token."); }
/// <summary> /// ASP.Net authentication & authorization, used by APIs and can be used /// by Services as well (for example, by SignalR hosts), but not by /// self-hosted services (e.g. with some custom TCP implementation). /// </summary> /// <returns></returns> public static IServiceCollection UseAspNetAuthentication( this IServiceCollection services, IAuthenticationInformationProvider authenticationInformationProvider) { services.AddTransient <RealtimeAuthenticationClient>(); var profileAuthentication = authenticationInformationProvider.GetProfileAuthenticationInformation(); var serviceAuthentication = authenticationInformationProvider.GetServiceAuthenticationInformation(); var additionalProfileAuthentications = authenticationInformationProvider.GetAdditionalProfileAuthenticationInformations(); if (profileAuthentication.TokenValidationParameters.ValidAudiences?.FirstOrDefault() == null || (serviceAuthentication != null && serviceAuthentication.TokenValidationParameters.ValidAudiences?.FirstOrDefault() == null) || additionalProfileAuthentications.Any(x => x.TokenValidationParameters.ValidAudiences?.FirstOrDefault() == null)) { throw new InvalidOperationException("Call UseSomeProvider method on AuthenticationInformationBuilder before calling this method, so that ValidAudiences parameter is set up."); } var authenticationSchemes = new List <string> { profileAuthentication.SchemeName ?? throw new InvalidOperationException("Scheme name is empty.") }; var authenticationBuilder = services .AddAuthentication(TyrAuthenticationSchemes.ProfileAuthenticationScheme) // Sets default authentication scheme. .AddJwtBearer(profileAuthentication.SchemeName, options => ConfigureOptions(options, profileAuthentication)); foreach (var additionalAuthentication in additionalProfileAuthentications) { authenticationSchemes.Add(additionalAuthentication.SchemeName ?? throw new InvalidOperationException("Scheme name is empty.")); authenticationBuilder.AddJwtBearer(additionalAuthentication.SchemeName, options => ConfigureOptions(options, additionalAuthentication)); } if (serviceAuthentication != null) { authenticationSchemes.Add(serviceAuthentication.SchemeName ?? throw new InvalidOperationException("Scheme name is empty.")); authenticationBuilder.AddJwtBearer(serviceAuthentication.SchemeName, options => ConfigureOptions(options, serviceAuthentication)); } if (authenticationSchemes.Distinct().Count() != authenticationSchemes.Count) { throw new InvalidOperationException("Duplicate authentication schemes found."); } services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .AddAuthenticationSchemes(authenticationSchemes.ToArray()) .Build(); }); services.AddTransient <IHttpContextAccessor, HttpContextAccessor>(); services.AddTransient <IProfileTokenService, HttpContextProfileTokenService>(); return(services); }