75 lines
2.8 KiB
C#
75 lines
2.8 KiB
C#
using Grpc.Core;
|
|
using Grpc.Core.Interceptors;
|
|
using Microsoft.Extensions.Options;
|
|
using MxGateway.Server.Configuration;
|
|
using MxGateway.Server.Security.Authentication;
|
|
|
|
namespace MxGateway.Server.Security.Authorization;
|
|
|
|
public sealed class GatewayGrpcAuthorizationInterceptor(
|
|
IApiKeyVerifier apiKeyVerifier,
|
|
GatewayGrpcScopeResolver scopeResolver,
|
|
IGatewayRequestIdentityAccessor identityAccessor,
|
|
IOptions<GatewayOptions> options) : Interceptor
|
|
{
|
|
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
|
|
TRequest request,
|
|
ServerCallContext context,
|
|
UnaryServerMethod<TRequest, TResponse> continuation)
|
|
{
|
|
ApiKeyIdentity? identity = await AuthenticateAndAuthorizeAsync(request, context).ConfigureAwait(false);
|
|
IDisposable? identityScope = identity is null ? null : identityAccessor.Push(identity);
|
|
using (identityScope)
|
|
{
|
|
return await continuation(request, context).ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
public override async Task ServerStreamingServerHandler<TRequest, TResponse>(
|
|
TRequest request,
|
|
IServerStreamWriter<TResponse> responseStream,
|
|
ServerCallContext context,
|
|
ServerStreamingServerMethod<TRequest, TResponse> continuation)
|
|
{
|
|
ApiKeyIdentity? identity = await AuthenticateAndAuthorizeAsync(request, context).ConfigureAwait(false);
|
|
IDisposable? identityScope = identity is null ? null : identityAccessor.Push(identity);
|
|
using (identityScope)
|
|
{
|
|
await continuation(request, responseStream, context).ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
private async Task<ApiKeyIdentity?> AuthenticateAndAuthorizeAsync<TRequest>(
|
|
TRequest request,
|
|
ServerCallContext context)
|
|
where TRequest : class
|
|
{
|
|
if (options.Value.Authentication.Mode == AuthenticationMode.Disabled)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string? authorizationHeader = context.RequestHeaders.GetValue("authorization");
|
|
ApiKeyVerificationResult verificationResult = await apiKeyVerifier
|
|
.VerifyAsync(authorizationHeader, context.CancellationToken)
|
|
.ConfigureAwait(false);
|
|
|
|
if (!verificationResult.Succeeded || verificationResult.Identity is null)
|
|
{
|
|
throw new RpcException(new Status(
|
|
StatusCode.Unauthenticated,
|
|
"Missing or invalid API key."));
|
|
}
|
|
|
|
string requiredScope = scopeResolver.ResolveRequiredScope(request);
|
|
if (!verificationResult.Identity.Scopes.Contains(requiredScope))
|
|
{
|
|
throw new RpcException(new Status(
|
|
StatusCode.PermissionDenied,
|
|
$"API key is missing required scope '{requiredScope}'."));
|
|
}
|
|
|
|
return verificationResult.Identity;
|
|
}
|
|
}
|