Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/grpc-js/src/auth-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2025 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import { PeerCertificate } from "tls";

export interface AuthContext {
transportSecurityType?: string;
sslPeerCertificate?: PeerCertificate;
}
2 changes: 2 additions & 0 deletions packages/grpc-js/src/call-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*
*/

import { AuthContext } from './auth-context';
import { CallCredentials } from './call-credentials';
import { Status } from './constants';
import { Deadline } from './deadline';
Expand Down Expand Up @@ -170,6 +171,7 @@ export interface Call {
halfClose(): void;
getCallNumber(): number;
setCredentials(credentials: CallCredentials): void;
getAuthContext(): AuthContext | null;
}

export interface DeadlineInfoProvider {
Expand Down
18 changes: 18 additions & 0 deletions packages/grpc-js/src/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { EmitterAugmentation1 } from './events';
import { Metadata } from './metadata';
import { ObjectReadable, ObjectWritable, WriteCallback } from './object-stream';
import { InterceptingCallInterface } from './client-interceptors';
import { AuthContext } from './auth-context';

/**
* A type extending the built-in Error object with additional fields.
Expand All @@ -37,6 +38,7 @@ export type SurfaceCall = {
call?: InterceptingCallInterface;
cancel(): void;
getPeer(): string;
getAuthContext(): AuthContext | null;
} & EmitterAugmentation1<'metadata', Metadata> &
EmitterAugmentation1<'status', StatusObject> &
EventEmitter;
Expand Down Expand Up @@ -100,6 +102,10 @@ export class ClientUnaryCallImpl
getPeer(): string {
return this.call?.getPeer() ?? 'unknown';
}

getAuthContext(): AuthContext | null {
return this.call?.getAuthContext() ?? null;
}
}

export class ClientReadableStreamImpl<ResponseType>
Expand All @@ -119,6 +125,10 @@ export class ClientReadableStreamImpl<ResponseType>
return this.call?.getPeer() ?? 'unknown';
}

getAuthContext(): AuthContext | null {
return this.call?.getAuthContext() ?? null;
}

_read(_size: number): void {
this.call?.startRead();
}
Expand All @@ -141,6 +151,10 @@ export class ClientWritableStreamImpl<RequestType>
return this.call?.getPeer() ?? 'unknown';
}

getAuthContext(): AuthContext | null {
return this.call?.getAuthContext() ?? null;
}

_write(chunk: RequestType, encoding: string, cb: WriteCallback) {
const context: MessageContext = {
callback: cb,
Expand Down Expand Up @@ -178,6 +192,10 @@ export class ClientDuplexStreamImpl<RequestType, ResponseType>
return this.call?.getPeer() ?? 'unknown';
}

getAuthContext(): AuthContext | null {
return this.call?.getAuthContext() ?? null;
}

_read(_size: number): void {
this.call?.startRead();
}
Expand Down
8 changes: 8 additions & 0 deletions packages/grpc-js/src/client-interceptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { Channel } from './channel';
import { CallOptions } from './client';
import { ClientMethodDefinition } from './make-client';
import { getErrorMessage } from './error';
import { AuthContext } from './auth-context';

/**
* Error class associated with passing both interceptors and interceptor
Expand Down Expand Up @@ -198,6 +199,7 @@ export interface InterceptingCallInterface {
sendMessage(message: any): void;
startRead(): void;
halfClose(): void;
getAuthContext(): AuthContext | null;
}

export class InterceptingCall implements InterceptingCallInterface {
Expand Down Expand Up @@ -338,6 +340,9 @@ export class InterceptingCall implements InterceptingCallInterface {
}
});
}
getAuthContext(): AuthContext | null {
return this.nextCall.getAuthContext();
}
}

function getCall(channel: Channel, path: string, options: CallOptions): Call {
Expand Down Expand Up @@ -427,6 +432,9 @@ class BaseInterceptingCall implements InterceptingCallInterface {
halfClose(): void {
this.call.halfClose();
}
getAuthContext(): AuthContext | null {
return this.call.getAuthContext();
}
}

/**
Expand Down
9 changes: 9 additions & 0 deletions packages/grpc-js/src/load-balancing-call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { splitHostPort } from './uri-parser';
import * as logging from './logging';
import { restrictControlPlaneStatusCode } from './control-plane-status';
import * as http2 from 'http2';
import { AuthContext } from './auth-context';

const TRACER_NAME = 'load_balancing_call';

Expand Down Expand Up @@ -375,4 +376,12 @@ export class LoadBalancingCall implements Call, DeadlineInfoProvider {
getCallNumber(): number {
return this.callNumber;
}

getAuthContext(): AuthContext | null {
if (this.child) {
return this.child.getAuthContext();
} else {
return null;
}
}
}
9 changes: 9 additions & 0 deletions packages/grpc-js/src/resolving-call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { InternalChannel } from './internal-channel';
import { Metadata } from './metadata';
import * as logging from './logging';
import { restrictControlPlaneStatusCode } from './control-plane-status';
import { AuthContext } from './auth-context';

const TRACER_NAME = 'resolving_call';

Expand Down Expand Up @@ -367,4 +368,12 @@ export class ResolvingCall implements Call {
getCallNumber(): number {
return this.callNumber;
}

getAuthContext(): AuthContext | null {
if (this.child) {
return this.child.getAuthContext();
} else {
return null;
}
}
}
8 changes: 8 additions & 0 deletions packages/grpc-js/src/retrying-call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
StatusObjectWithProgress,
} from './load-balancing-call';
import { InternalChannel } from './internal-channel';
import { AuthContext } from './auth-context';

const TRACER_NAME = 'retrying_call';

Expand Down Expand Up @@ -859,4 +860,11 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
getHost(): string {
return this.host;
}
getAuthContext(): AuthContext | null {
if (this.committedCallIndex !== null) {
return this.underlyingCalls[this.committedCallIndex].call.getAuthContext();
} else {
return null;
}
}
}
18 changes: 18 additions & 0 deletions packages/grpc-js/src/server-call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type { ObjectReadable, ObjectWritable } from './object-stream';
import type { StatusObject, PartialStatusObject } from './call-interface';
import type { Deadline } from './deadline';
import type { ServerInterceptingCallInterface } from './server-interceptors';
import { AuthContext } from './auth-context';

export type ServerStatusResponse = Partial<StatusObject>;

Expand All @@ -38,6 +39,7 @@ export type ServerSurfaceCall = {
getDeadline(): Deadline;
getPath(): string;
getHost(): string;
getAuthContext(): AuthContext;
} & EventEmitter;

export type ServerUnaryCall<RequestType, ResponseType> = ServerSurfaceCall & {
Expand Down Expand Up @@ -114,6 +116,10 @@ export class ServerUnaryCallImpl<RequestType, ResponseType>
getHost(): string {
return this.call.getHost();
}

getAuthContext(): AuthContext {
return this.call.getAuthContext();
}
}

export class ServerReadableStreamImpl<RequestType, ResponseType>
Expand Down Expand Up @@ -154,6 +160,10 @@ export class ServerReadableStreamImpl<RequestType, ResponseType>
getHost(): string {
return this.call.getHost();
}

getAuthContext(): AuthContext {
return this.call.getAuthContext();
}
}

export class ServerWritableStreamImpl<RequestType, ResponseType>
Expand Down Expand Up @@ -203,6 +213,10 @@ export class ServerWritableStreamImpl<RequestType, ResponseType>
return this.call.getHost();
}

getAuthContext(): AuthContext {
return this.call.getAuthContext();
}

_write(
chunk: ResponseType,
encoding: string,
Expand Down Expand Up @@ -276,6 +290,10 @@ export class ServerDuplexStreamImpl<RequestType, ResponseType>
return this.call.getHost();
}

getAuthContext(): AuthContext {
return this.call.getAuthContext();
}

_read(size: number) {
this.call.startRead();
}
Expand Down
19 changes: 19 additions & 0 deletions packages/grpc-js/src/server-interceptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import * as zlib from 'zlib';
import { StreamDecoder } from './stream-decoder';
import { CallEventTracker } from './transport';
import * as logging from './logging';
import { AuthContext } from './auth-context';
import { TLSSocket } from 'tls';

const TRACER_NAME = 'server_call';

Expand Down Expand Up @@ -332,6 +334,10 @@ export interface ServerInterceptingCallInterface {
* Return the host requested by the client in the ":authority" header.
*/
getHost(): string;
/**
* Return the auth context of the connection the call is associated with.
*/
getAuthContext(): AuthContext;
}

export class ServerInterceptingCall implements ServerInterceptingCallInterface {
Expand Down Expand Up @@ -440,6 +446,9 @@ export class ServerInterceptingCall implements ServerInterceptingCallInterface {
getHost(): string {
return this.nextCall.getHost();
}
getAuthContext(): AuthContext {
return this.nextCall.getAuthContext();
}
}

export interface ServerInterceptor {
Expand Down Expand Up @@ -971,6 +980,16 @@ export class BaseServerInterceptingCall
getHost(): string {
return this.host;
}
getAuthContext(): AuthContext {
if (this.stream.session?.socket instanceof TLSSocket) {
return {
transportSecurityType: 'ssl',
sslPeerCertificate: this.stream.session.socket.getPeerCertificate()
}
} else {
return {};
}
}
}

export function getServerInterceptingCall(
Expand Down
6 changes: 6 additions & 0 deletions packages/grpc-js/src/subchannel-call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
WriteCallback,
} from './call-interface';
import { CallEventTracker, Transport } from './transport';
import { AuthContext } from './auth-context';

const TRACER_NAME = 'subchannel_call';

Expand Down Expand Up @@ -71,6 +72,7 @@ export interface SubchannelCall {
halfClose(): void;
getCallNumber(): number;
getDeadlineInfo(): string[];
getAuthContext(): AuthContext;
}

export interface StatusObjectWithRstCode extends StatusObject {
Expand Down Expand Up @@ -556,6 +558,10 @@ export class Http2SubchannelCall implements SubchannelCall {
return this.callId;
}

getAuthContext(): AuthContext {
return this.transport.getAuthContext();
}

startRead() {
/* If the stream has ended with an error, we should not emit any more
* messages and we should communicate that the stream has ended */
Expand Down
17 changes: 17 additions & 0 deletions packages/grpc-js/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
import { Metadata } from './metadata';
import { getNextCallNumber } from './call-number';
import { Socket } from 'net';
import { AuthContext } from './auth-context';

const TRACER_NAME = 'transport';
const FLOW_CONTROL_TRACER_NAME = 'transport_flowctrl';
Expand Down Expand Up @@ -83,6 +84,7 @@ export interface Transport {
getChannelzRef(): SocketRef;
getPeerName(): string;
getOptions(): ChannelOptions;
getAuthContext(): AuthContext;
createCall(
metadata: Metadata,
host: string,
Expand Down Expand Up @@ -129,6 +131,8 @@ class Http2Transport implements Transport {

private disconnectHandled = false;

private authContext: AuthContext;

// Channelz info
private channelzRef: SocketRef;
private readonly channelzEnabled: boolean = true;
Expand Down Expand Up @@ -254,6 +258,15 @@ class Http2Transport implements Transport {
if (this.keepaliveWithoutCalls) {
this.maybeStartKeepalivePingTimer();
}

if (session.socket instanceof TLSSocket) {
this.authContext = {
transportSecurityType: 'ssl',
sslPeerCertificate: session.socket.getPeerCertificate()
};
} else {
this.authContext = {};
}
}

private getChannelzInfo(): SocketInfo {
Expand Down Expand Up @@ -622,6 +635,10 @@ class Http2Transport implements Transport {
return this.options;
}

getAuthContext(): AuthContext {
return this.authContext;
}

shutdown() {
this.session.close();
unregisterChannelzRef(this.channelzRef);
Expand Down
Loading