Uwierzytelnianie i autoryzacja w gRPC dla ASP.NET Core
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Ostrzeżenie
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz .NET i .NET Core Support Policy (Zasady obsługi platformy .NET Core). Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Autor: James Newton-King
Wyświetl lub pobierz przykładowy kod (jak pobrać)
Uwierzytelnianie użytkowników wywołujących usługę gRPC
GRPC można używać z uwierzytelnianiem ASP.NET Core w celu skojarzenia użytkownika z każdym wywołaniem.
Poniżej przedstawiono przykład użycia Program.cs
uwierzytelniania gRPC i ASP.NET Core:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapGrpcService<GreeterService>();
Uwaga
Kolejność rejestrowania oprogramowania pośredniczącego ASP.NET Core ma znaczenie. Zawsze dzwonij UseAuthentication
i UseAuthorization
po UseRouting
i przed UseEndpoints
.
Mechanizm uwierzytelniania używany przez aplikację podczas wywołania musi być skonfigurowany. Konfiguracja uwierzytelniania jest dodawana i Program.cs
będzie się różnić w zależności od mechanizmu uwierzytelniania używanego przez aplikację.
Po skonfigurowaniu uwierzytelniania można uzyskać dostęp do użytkownika w metodach usługi gRPC za pośrednictwem .ServerCallContext
public override Task<BuyTicketsResponse> BuyTickets(
BuyTicketsRequest request, ServerCallContext context)
{
var user = context.GetHttpContext().User;
// ... access data from ClaimsPrincipal ...
}
Uwierzytelnianie tokenu elementu nośnego
Klient może zapewnić token dostępu do uwierzytelniania. Serwer weryfikuje token i używa go do identyfikowania użytkownika.
Na serwerze uwierzytelnianie tokenu elementu nośnego jest konfigurowane przy użyciu oprogramowania pośredniczącego elementu nośnego JWT.
W kliencie gRPC platformy .NET token można wysyłać z wywołaniami przy użyciu kolekcji Metadata
. Wpisy w Metadata
kolekcji są wysyłane za pomocą wywołania gRPC jako nagłówków HTTP:
public bool DoAuthenticatedCall(
Ticketer.TicketerClient client, string token)
{
var headers = new Metadata();
headers.Add("Authorization", $"Bearer {token}");
var request = new BuyTicketsRequest { Count = 1 };
var response = await client.BuyTicketsAsync(request, headers);
return response.Success;
}
Ustawianie tokenu elementu nośnego za pomocą polecenia CallCredentials
Konfigurowanie ChannelCredentials
w kanale jest alternatywnym sposobem wysyłania tokenu do usługi za pomocą wywołań gRPC. Element ChannelCredentials
może zawierać CallCredentials
element , który umożliwia automatyczne ustawianie Metadata
elementu .
Zalety korzystania z programu CallCredentials
:
- Uwierzytelnianie jest konfigurowane centralnie w kanale. Token nie musi być udostępniany ręcznie wywołaniu gRPC.
- Wywołanie
CallCredentials.FromInterceptor
zwrotne jest asynchroniczne. W razie potrzeby poświadczenia wywołania mogą pobierać token poświadczeń z systemu zewnętrznego. Metody asynchroniczne wewnątrz wywołania zwrotnego powinny używać metodyCancellationToken
na .AuthInterceptorContext
Uwaga
CallCredentials
są stosowane tylko wtedy, gdy kanał jest zabezpieczony przy użyciu protokołu TLS. Wysyłanie nagłówków uwierzytelniania za pośrednictwem niezabezpieczonego połączenia ma wpływ na bezpieczeństwo i nie powinno być wykonywane w środowiskach produkcyjnych. Aplikacja może skonfigurować kanał tak, aby ignorował to zachowanie i zawsze używać CallCredentials
go przez ustawienie UnsafeUseInsecureChannelCallCredentials
w kanale.
Poświadczenie w poniższym przykładzie konfiguruje kanał do wysyłania tokenu z każdym wywołaniem gRPC:
private static GrpcChannel CreateAuthenticatedChannel(ITokenProvder tokenProvider)
{
var credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
{
var token = await tokenProvider.GetTokenAsync(context.CancellationToken);
metadata.Add("Authorization", $"Bearer {token}");
});
var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
{
Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
});
return channel;
}
Token elementu nośnego z fabryką klienta gRPC
Fabryka klienta gRPC może tworzyć klientów, którzy wysyłają token elementu nośnego przy użyciu polecenia AddCallCredentials
. Ta metoda jest dostępna w grpc.Net.ClientFactory w wersji 2.46.0 lub nowszej.
Delegat przekazany do AddCallCredentials
jest wykonywany dla każdego wywołania gRPC:
builder.Services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
})
.AddCallCredentials((context, metadata) =>
{
if (!string.IsNullOrEmpty(_token))
{
metadata.Add("Authorization", $"Bearer {_token}");
}
return Task.CompletedTask;
});
Wstrzykiwanie zależności (DI) można połączyć z AddCallCredentials
. Przeciążenie przechodzi IServiceProvider
do delegata, który może służyć do pobierania usługi skonstruowanej z di przy użyciu usług o określonym zakresie i przejściowych.
Rozważ aplikację, która ma następujące kwestie:
- Zdefiniowana przez
ITokenProvider
użytkownika na potrzeby uzyskiwania tokenu elementu nośnego.ITokenProvider
jest rejestrowany w di z okresem istnienia o określonym zakresie. - Fabryka klienta gRPC jest skonfigurowana do tworzenia klientów, którzy są wstrzykiwani do usług gRPC i kontrolerów internetowego interfejsu API.
- Wywołania gRPC powinny służyć
ITokenProvider
do uzyskania tokenu elementu nośnego.
public interface ITokenProvider
{
Task<string> GetTokenAsync(CancellationToken cancellationToken);
}
public class AppTokenProvider : ITokenProvider
{
private string _token;
public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
{
if (_token == null)
{
// App code to resolve the token here.
}
return _token;
}
}
builder.Services.AddScoped<ITokenProvider, AppTokenProvider>();
builder.Services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
})
.AddCallCredentials(async (context, metadata, serviceProvider) =>
{
var provider = serviceProvider.GetRequiredService<ITokenProvider>();
var token = await provider.GetTokenAsync(context.CancellationToken);
metadata.Add("Authorization", $"Bearer {token}");
}));
Powyższy kod ma następujące działanie:
- Definiuje
ITokenProvider
iAppTokenProvider
. Te typy obsługują rozpoznawanie tokenu uwierzytelniania dla wywołań gRPC. - Rejestruje
AppTokenProvider
typ z di w okresie istnienia o określonym zakresie.AppTokenProvider
Buforuje token, tak aby tylko pierwsze wywołanie w zakresie było wymagane do jego obliczenia. - Rejestruje typ w
GreeterClient
fabryce klienta. - Konfiguruje
AddCallCredentials
dla tego klienta. Delegat jest wykonywany za każdym razem, gdy jest wykonywane wywołanie i dodaje token zwracany przezITokenProvider
element do metadanych.
Uwierzytelnianie certyfikatów klientów
Klient może również podać certyfikat klienta na potrzeby uwierzytelniania. Uwierzytelnianie certyfikatu odbywa się na poziomie protokołu TLS, na długo przed rozpoczęciem ASP.NET Core. Po wprowadzeniu żądania ASP.NET Core pakiet uwierzytelniania certyfikatu klienta umożliwia rozpoznawanie certyfikatu na ClaimsPrincipal
.
Uwaga
Skonfiguruj serwer tak, aby akceptował certyfikaty klienta. Aby uzyskać informacje na temat akceptowania certyfikatów klienta w Kestrelusługach IIS i na platformie Azure, zobacz Konfigurowanie uwierzytelniania certyfikatów w programie ASP.NET Core.
W kliencie gRPC platformy .NET certyfikat klienta jest dodawany do HttpClientHandler
tego certyfikatu, a następnie używany do tworzenia klienta gRPC:
public Ticketer.TicketerClient CreateClientWithCert(
string baseAddress,
X509Certificate2 certificate)
{
// Add client cert to the handler
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(certificate);
// Create the gRPC channel
var channel = GrpcChannel.ForAddress(baseAddress, new GrpcChannelOptions
{
HttpHandler = handler
});
return new Ticketer.TicketerClient(channel);
}
Inne mechanizmy uwierzytelniania
Wiele ASP.NET Core obsługiwanych mechanizmów uwierzytelniania działa z gRPC:
- Microsoft Entra ID
- Certyfikat klienta
- IdentityServer
- JWT Token
- OAuth 2.0
- OpenID Connect
- WS-Federation
Aby uzyskać więcej informacji na temat konfigurowania uwierzytelniania na serwerze, zobacz ASP.NET Core authentication (Uwierzytelnianie podstawowe ASP.NET).
Skonfigurowanie klienta gRPC do korzystania z uwierzytelniania będzie zależeć od używanego mechanizmu uwierzytelniania. Poprzednie przykłady tokenu elementu nośnego i certyfikatu klienta pokazują kilka sposobów konfigurowania klienta gRPC do wysyłania metadanych uwierzytelniania za pomocą wywołań gRPC:
- Silnie typizowane klienci gRPC używają
HttpClient
wewnętrznie. Uwierzytelnianie można skonfigurować w systemie HttpClientHandlerlub przez dodanie wystąpień niestandardowych HttpMessageHandler do elementuHttpClient
. - Każde wywołanie gRPC ma opcjonalny
CallOptions
argument. Nagłówki niestandardowe można wysyłać przy użyciu kolekcji nagłówków opcji.
Uwaga
Uwierzytelnianie systemu Windows (NTLM/Kerberos/Negotiate) nie może być używane z gRPC. Usługa gRPC wymaga protokołu HTTP/2, a protokół HTTP/2 nie obsługuje uwierzytelniania systemu Windows.
Autoryzowanie użytkowników do uzyskiwania dostępu do usług i metod usług
Domyślnie wszystkie metody w usłudze mogą być wywoływane przez nieuwierzytelnionych użytkowników. Aby wymagać uwierzytelniania, zastosuj [Authorize]
atrybut do usługi:
[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
}
Można użyć argumentów konstruktora i właściwości atrybutu [Authorize]
, aby ograniczyć dostęp tylko do użytkowników pasujących do określonych zasad autoryzacji. Jeśli na przykład masz niestandardowe zasady autoryzacji o nazwie MyAuthorizationPolicy
, upewnij się, że tylko użytkownicy pasujący do tych zasad mogą uzyskiwać dostęp do usługi przy użyciu następującego kodu:
[Authorize("MyAuthorizationPolicy")]
public class TicketerService : Ticketer.TicketerBase
{
}
Poszczególne metody usług mogą również mieć [Authorize]
zastosowany atrybut. Jeśli bieżący użytkownik nie jest zgodny z zasadami zastosowanymi zarówno do metody, jak i klasy, zostanie zwrócony błąd do obiektu wywołującego:
[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
public override Task<AvailableTicketsResponse> GetAvailableTickets(
Empty request, ServerCallContext context)
{
// ... buy tickets for the current user ...
}
[Authorize("Administrators")]
public override Task<BuyTicketsResponse> RefundTickets(
BuyTicketsRequest request, ServerCallContext context)
{
// ... refund tickets (something only Administrators can do) ..
}
}
Dodatkowe zasoby
Wyświetl lub pobierz przykładowy kod (jak pobrać)
Uwierzytelnianie użytkowników wywołujących usługę gRPC
GRPC można używać z uwierzytelnianiem ASP.NET Core w celu skojarzenia użytkownika z każdym wywołaniem.
Poniżej przedstawiono przykład użycia Startup.Configure
uwierzytelniania gRPC i ASP.NET Core:
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>();
});
}
Uwaga
Kolejność rejestrowania oprogramowania pośredniczącego ASP.NET Core ma znaczenie. Zawsze dzwonij UseAuthentication
i UseAuthorization
po UseRouting
i przed UseEndpoints
.
Mechanizm uwierzytelniania używany przez aplikację podczas wywołania musi być skonfigurowany. Konfiguracja uwierzytelniania jest dodawana i Startup.ConfigureServices
będzie się różnić w zależności od mechanizmu uwierzytelniania używanego przez aplikację.
Po skonfigurowaniu uwierzytelniania można uzyskać dostęp do użytkownika w metodach usługi gRPC za pośrednictwem .ServerCallContext
public override Task<BuyTicketsResponse> BuyTickets(
BuyTicketsRequest request, ServerCallContext context)
{
var user = context.GetHttpContext().User;
// ... access data from ClaimsPrincipal ...
}
Uwierzytelnianie tokenu elementu nośnego
Klient może zapewnić token dostępu do uwierzytelniania. Serwer weryfikuje token i używa go do identyfikowania użytkownika.
Na serwerze uwierzytelnianie tokenu elementu nośnego jest konfigurowane przy użyciu oprogramowania pośredniczącego elementu nośnego JWT.
W kliencie gRPC platformy .NET token można wysyłać z wywołaniami przy użyciu kolekcji Metadata
. Wpisy w Metadata
kolekcji są wysyłane za pomocą wywołania gRPC jako nagłówków HTTP:
public bool DoAuthenticatedCall(
Ticketer.TicketerClient client, string token)
{
var headers = new Metadata();
headers.Add("Authorization", $"Bearer {token}");
var request = new BuyTicketsRequest { Count = 1 };
var response = await client.BuyTicketsAsync(request, headers);
return response.Success;
}
Ustawianie tokenu elementu nośnego za pomocą polecenia CallCredentials
Konfigurowanie ChannelCredentials
w kanale jest alternatywnym sposobem wysyłania tokenu do usługi za pomocą wywołań gRPC. Element ChannelCredentials
może zawierać CallCredentials
element , który umożliwia automatyczne ustawianie Metadata
elementu .
Zalety korzystania z programu CallCredentials
:
- Uwierzytelnianie jest konfigurowane centralnie w kanale. Token nie musi być udostępniany ręcznie wywołaniu gRPC.
- Wywołanie
CallCredentials.FromInterceptor
zwrotne jest asynchroniczne. W razie potrzeby poświadczenia wywołania mogą pobierać token poświadczeń z systemu zewnętrznego. Metody asynchroniczne wewnątrz wywołania zwrotnego powinny używać metodyCancellationToken
na .AuthInterceptorContext
Uwaga
CallCredentials
są stosowane tylko wtedy, gdy kanał jest zabezpieczony przy użyciu protokołu TLS. Wysyłanie nagłówków uwierzytelniania za pośrednictwem niezabezpieczonego połączenia ma wpływ na bezpieczeństwo i nie powinno być wykonywane w środowiskach produkcyjnych. Aplikacja może skonfigurować kanał tak, aby ignorował to zachowanie i zawsze używać CallCredentials
go przez ustawienie UnsafeUseInsecureChannelCallCredentials
w kanale.
Poświadczenie w poniższym przykładzie konfiguruje kanał do wysyłania tokenu z każdym wywołaniem gRPC:
private static GrpcChannel CreateAuthenticatedChannel(ITokenProvder tokenProvider)
{
var credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
{
var token = await tokenProvider.GetTokenAsync(context.CancellationToken);
metadata.Add("Authorization", $"Bearer {token}");
});
var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
{
Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
});
return channel;
}
Token elementu nośnego z fabryką klienta gRPC
Fabryka klienta gRPC może tworzyć klientów, którzy wysyłają token elementu nośnego przy użyciu polecenia AddCallCredentials
. Ta metoda jest dostępna w grpc.Net.ClientFactory w wersji 2.46.0 lub nowszej.
Delegat przekazany do AddCallCredentials
jest wykonywany dla każdego wywołania gRPC:
services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
})
.AddCallCredentials((context, metadata) =>
{
if (!string.IsNullOrEmpty(_token))
{
metadata.Add("Authorization", $"Bearer {_token}");
}
return Task.CompletedTask;
});
Wstrzykiwanie zależności (DI) można połączyć z AddCallCredentials
. Przeciążenie przechodzi IServiceProvider
do delegata, który może służyć do pobierania usługi skonstruowanej z di przy użyciu usług o określonym zakresie i przejściowych.
Rozważ aplikację, która ma następujące kwestie:
- Zdefiniowana przez
ITokenProvider
użytkownika na potrzeby uzyskiwania tokenu elementu nośnego.ITokenProvider
jest rejestrowany w di z okresem istnienia o określonym zakresie. - Fabryka klienta gRPC jest skonfigurowana do tworzenia klientów, którzy są wstrzykiwani do usług gRPC i kontrolerów internetowego interfejsu API.
- Wywołania gRPC powinny służyć
ITokenProvider
do uzyskania tokenu elementu nośnego.
public interface ITokenProvider
{
Task<string> GetTokenAsync(CancellationToken cancellationToken);
}
public class AppTokenProvider : ITokenProvider
{
private string _token;
public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
{
if (_token == null)
{
// App code to resolve the token here.
}
return _token;
}
}
services.AddScoped<ITokenProvider, AppTokenProvider>();
services
.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
})
.AddCallCredentials(async (context, metadata, serviceProvider) =>
{
var provider = serviceProvider.GetRequiredService<ITokenProvider>();
var token = await provider.GetTokenAsync(context.CancellationToken);
metadata.Add("Authorization", $"Bearer {token}");
}));
Powyższy kod ma następujące działanie:
- Definiuje
ITokenProvider
iAppTokenProvider
. Te typy obsługują rozpoznawanie tokenu uwierzytelniania dla wywołań gRPC. - Rejestruje
AppTokenProvider
typ z di w okresie istnienia o określonym zakresie.AppTokenProvider
Buforuje token, tak aby tylko pierwsze wywołanie w zakresie było wymagane do jego obliczenia. - Rejestruje typ w
GreeterClient
fabryce klienta. - Konfiguruje
AddCallCredentials
dla tego klienta. Delegat jest wykonywany za każdym razem, gdy jest wykonywane wywołanie i dodaje token zwracany przezITokenProvider
element do metadanych.
Uwierzytelnianie certyfikatów klientów
Klient może również podać certyfikat klienta na potrzeby uwierzytelniania. Uwierzytelnianie certyfikatu odbywa się na poziomie protokołu TLS, na długo przed rozpoczęciem ASP.NET Core. Po wprowadzeniu żądania ASP.NET Core pakiet uwierzytelniania certyfikatu klienta umożliwia rozpoznawanie certyfikatu na ClaimsPrincipal
.
Uwaga
Skonfiguruj serwer tak, aby akceptował certyfikaty klienta. Aby uzyskać informacje na temat akceptowania certyfikatów klienta w Kestrelusługach IIS i na platformie Azure, zobacz Konfigurowanie uwierzytelniania certyfikatów w programie ASP.NET Core.
W kliencie gRPC platformy .NET certyfikat klienta jest dodawany do HttpClientHandler
tego certyfikatu, a następnie używany do tworzenia klienta gRPC:
public Ticketer.TicketerClient CreateClientWithCert(
string baseAddress,
X509Certificate2 certificate)
{
// Add client cert to the handler
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(certificate);
// Create the gRPC channel
var channel = GrpcChannel.ForAddress(baseAddress, new GrpcChannelOptions
{
HttpHandler = handler
});
return new Ticketer.TicketerClient(channel);
}
Inne mechanizmy uwierzytelniania
Wiele ASP.NET Core obsługiwanych mechanizmów uwierzytelniania działa z gRPC:
- Microsoft Entra ID
- Certyfikat klienta
- IdentityServer
- JWT Token
- OAuth 2.0
- OpenID Connect
- WS-Federation
Aby uzyskać więcej informacji na temat konfigurowania uwierzytelniania na serwerze, zobacz ASP.NET Core authentication (Uwierzytelnianie podstawowe ASP.NET).
Skonfigurowanie klienta gRPC do korzystania z uwierzytelniania będzie zależeć od używanego mechanizmu uwierzytelniania. Poprzednie przykłady tokenu elementu nośnego i certyfikatu klienta pokazują kilka sposobów konfigurowania klienta gRPC do wysyłania metadanych uwierzytelniania za pomocą wywołań gRPC:
- Silnie typizowane klienci gRPC używają
HttpClient
wewnętrznie. Uwierzytelnianie można skonfigurować w systemie HttpClientHandlerlub przez dodanie wystąpień niestandardowych HttpMessageHandler do elementuHttpClient
. - Każde wywołanie gRPC ma opcjonalny
CallOptions
argument. Nagłówki niestandardowe można wysyłać przy użyciu kolekcji nagłówków opcji.
Uwaga
Uwierzytelnianie systemu Windows (NTLM/Kerberos/Negotiate) nie może być używane z gRPC. Usługa gRPC wymaga protokołu HTTP/2, a protokół HTTP/2 nie obsługuje uwierzytelniania systemu Windows.
Autoryzowanie użytkowników do uzyskiwania dostępu do usług i metod usług
Domyślnie wszystkie metody w usłudze mogą być wywoływane przez nieuwierzytelnionych użytkowników. Aby wymagać uwierzytelniania, zastosuj [Authorize]
atrybut do usługi:
[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
}
Można użyć argumentów konstruktora i właściwości atrybutu [Authorize]
, aby ograniczyć dostęp tylko do użytkowników pasujących do określonych zasad autoryzacji. Jeśli na przykład masz niestandardowe zasady autoryzacji o nazwie MyAuthorizationPolicy
, upewnij się, że tylko użytkownicy pasujący do tych zasad mogą uzyskiwać dostęp do usługi przy użyciu następującego kodu:
[Authorize("MyAuthorizationPolicy")]
public class TicketerService : Ticketer.TicketerBase
{
}
Poszczególne metody usług mogą również mieć [Authorize]
zastosowany atrybut. Jeśli bieżący użytkownik nie jest zgodny z zasadami zastosowanymi zarówno do metody, jak i klasy, zostanie zwrócony błąd do obiektu wywołującego:
[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
public override Task<AvailableTicketsResponse> GetAvailableTickets(
Empty request, ServerCallContext context)
{
// ... buy tickets for the current user ...
}
[Authorize("Administrators")]
public override Task<BuyTicketsResponse> RefundTickets(
BuyTicketsRequest request, ServerCallContext context)
{
// ... refund tickets (something only Administrators can do) ..
}
}
Metody rozszerzenia autoryzacji
Autorizaton można również kontrolować przy użyciu standardowych metod rozszerzenia autoryzacji ASP.NET Core, takich jak AllowAnonymous
i RequireAuthorization
.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
app.MapGrpcService<TicketerService>().RequireAuthorization("Administrators");
app.Run();