(ctx context.Context, unauthenticatedMethods []string)
| 35 | } |
| 36 | |
| 37 | func AuthGrpcMiddleware(ctx context.Context, unauthenticatedMethods []string) (grpc.UnaryServerInterceptor, error) { |
| 38 | oauthConfig := internal.GetPeerDBOAuthConfig(ctx) |
| 39 | oauthJwtClaims := map[string]string{} |
| 40 | if oauthConfig.OAuthJwtClaimKey != "" { |
| 41 | oauthJwtClaims[oauthConfig.OAuthJwtClaimKey] = oauthConfig.OAuthClaimValue |
| 42 | } |
| 43 | cfg := AuthenticationConfig{ |
| 44 | Enabled: oauthConfig.OAuthIssuerUrl != "", |
| 45 | KeySetJSON: oauthConfig.KeySetJson, |
| 46 | OAuthDiscoveryEnabled: oauthConfig.OAuthDiscoveryEnabled, |
| 47 | OAuthIssuerUrl: oauthConfig.OAuthIssuerUrl, |
| 48 | OauthJwtCustomClaims: oauthJwtClaims, |
| 49 | } |
| 50 | // load identity providers before checking if authentication is enabled so configuration can be validated |
| 51 | ip, err := identityProvidersFromConfig(ctx, cfg) |
| 52 | |
| 53 | if !cfg.Enabled { |
| 54 | if err != nil { // if there was an error loading identity providers, warn only if authentication is disabled |
| 55 | slog.WarnContext(ctx, "OAuth is disabled", slog.Any("error", err)) |
| 56 | } |
| 57 | |
| 58 | slog.WarnContext(ctx, "authentication is disabled") |
| 59 | |
| 60 | return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { |
| 61 | return handler(ctx, req) |
| 62 | }, nil |
| 63 | } |
| 64 | |
| 65 | if err != nil { |
| 66 | return nil, err |
| 67 | } |
| 68 | |
| 69 | unauthenticatedMethodsMap := make(map[string]struct{}, len(unauthenticatedMethods)) |
| 70 | for _, method := range unauthenticatedMethods { |
| 71 | unauthenticatedMethodsMap[method] = struct{}{} |
| 72 | } |
| 73 | return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { |
| 74 | if _, unauthorized := unauthenticatedMethodsMap[info.FullMethod]; !unauthorized { |
| 75 | var authHeader string |
| 76 | authHeaders := metadata.ValueFromIncomingContext(ctx, "Authorization") |
| 77 | if len(authHeaders) == 1 { |
| 78 | authHeader = authHeaders[0] |
| 79 | } else if len(authHeaders) > 1 { |
| 80 | slog.WarnContext(ctx, "Multiple Authorization headers supplied, request rejected", slog.String("method", info.FullMethod)) |
| 81 | return nil, status.Errorf(codes.Unauthenticated, "multiple Authorization headers supplied, request rejected") |
| 82 | } |
| 83 | if _, err := validateRequestToken(authHeader, cfg.OauthJwtCustomClaims, ip...); err != nil { |
| 84 | slog.DebugContext(ctx, "Failed to validate request token", slog.String("method", info.FullMethod), slog.Any("error", err)) |
| 85 | return nil, status.Error(codes.Unauthenticated, err.Error()) |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | return handler(ctx, req) |
| 90 | }, nil |
| 91 | } |
| 92 | |
| 93 | func validateRequestToken(authHeader string, claims map[string]string, ip ...identityProvider) ([]byte, error) { |
| 94 | payload, err := jwtFromRequest(authHeader) |
nothing calls this directly
no test coverage detected