@@ -6,20 +6,153 @@ package kotlinx.rpc.grpc.client
66
77import kotlinx.rpc.grpc.GrpcMetadata
88
9+ /* *
10+ * Provides per-call authentication credentials for gRPC calls.
11+ *
12+ * Call credentials are used to attach authentication information (such as tokens, API keys, or signatures)
13+ * to individual gRPC calls through metadata headers. Unlike client credentials, which establish
14+ * the transport security layer (e.g., TLS), call credentials operate at the application layer
15+ * and can be dynamically generated for each request.
16+ *
17+ * ## Usage
18+ *
19+ * Implement this interface to create custom authentication mechanisms:
20+ *
21+ * ```kotlin
22+ * class BearerTokenCredentials(private val token: String) : GrpcCallCredentials {
23+ * override suspend fun GrpcMetadata.applyOnMetadata(callOptions: GrpcCallOptions) {
24+ * append("Authorization", "Bearer $token")
25+ * }
26+ * }
27+ * ```
28+ *
29+ * Credentials can be combined using the [plus] operator or [combine] function:
30+ *
31+ * ```kotlin
32+ * val credentials = TlsClientCredentials(...) + BearerTokenCredentials("my-token")
33+ * ```
34+ *
35+ * ## Transport Security
36+ *
37+ * By default, call credentials require transport security (TLS) to prevent credential leakage.
38+ * Override [requiresTransportSecurity] to `false` only for testing or non-production environments.
39+ *
40+ * @see applyOnMetadata
41+ * @see requiresTransportSecurity
42+ * @see plus
43+ * @see combine
44+ */
945public interface GrpcCallCredentials {
1046
47+ /* *
48+ * Applies authentication metadata to the gRPC call.
49+ *
50+ * This method is invoked before each gRPC call to add authentication headers or metadata.
51+ * Implementations should append the necessary authentication information to the [GrpcMetadata] receiver.
52+ *
53+ * The method is suspending to allow asynchronous token retrieval or refresh operations,
54+ * such as fetching tokens from secure storage or performing OAuth token exchanges.
55+ *
56+ * ## Examples
57+ *
58+ * Adding a bearer token:
59+ * ```kotlin
60+ * override suspend fun GrpcMetadata.applyOnMetadata(callOptions: GrpcCallOptions) {
61+ * append("Authorization", "Bearer $token")
62+ * }
63+ * ```
64+ *
65+ * Throwing a [kotlinx.rpc.grpc.StatusException] to fail the call:
66+ * ```kotlin
67+ * override suspend fun GrpcMetadata.applyOnMetadata(callOptions: GrpcCallOptions) {
68+ * if (!isValid) {
69+ * throw StatusException(Status(StatusCode.UNAUTHENTICATED, "Invalid credentials"))
70+ * }
71+ * append("Authorization", "Bearer $token")
72+ * }
73+ * ```
74+ *
75+ * @param callOptions The options for the current call, providing context and configuration.
76+ * @receiver The metadata to which authentication information should be added.
77+ * @throws kotlinx.rpc.grpc.StatusException to abort the call with a specific gRPC status.
78+ */
1179 public suspend fun GrpcMetadata.applyOnMetadata (callOptions : GrpcCallOptions )
1280
81+ /* *
82+ * Indicates whether this credential requires transport security (TLS).
83+ *
84+ * When `true` (the default), the credential will only be applied to calls over secure transports.
85+ * If transport security is not present, the call will fail with `UNAUTHENTICATED`.
86+ *
87+ * Set to `false` only for credentials that are safe to send over insecure connections,
88+ * such as in testing environments or for non-sensitive authentication mechanisms.
89+ *
90+ * @return `true` if transport security is required, `false` otherwise.
91+ */
1392 public val requiresTransportSecurity: Boolean
1493 get() = true
1594}
1695
96+ /* *
97+ * Combines two call credentials into a single credential that applies both.
98+ *
99+ * The resulting credential will apply both sets of credentials in order, allowing
100+ * multiple authentication mechanisms to be used simultaneously. For example,
101+ * combining channel credentials with call credentials, or applying multiple
102+ * authentication headers to the same call.
103+ *
104+ * The combined credential requires transport security if either of the original
105+ * credentials requires it.
106+ *
107+ * ## Example
108+ *
109+ * ```kotlin
110+ * val tlsCreds = TlsClientCredentials { ... }
111+ * val bearerToken = BearerTokenCredentials("my-token")
112+ * val combined = tlsCreds + bearerToken
113+ * ```
114+ *
115+ * Multiple credentials can be chained:
116+ * ```kotlin
117+ * val combined = creds1 + creds2 + creds3
118+ * ```
119+ *
120+ * @param other The credential to combine with this one.
121+ * @return A new credential that applies both credentials.
122+ * @see combine
123+ */
17124public operator fun GrpcCallCredentials.plus (other : GrpcCallCredentials ): GrpcCallCredentials {
18125 return CombinedCallCredentials (this , other)
19126}
20127
128+ /* *
129+ * Combines two call credentials into a single credential that applies both.
130+ *
131+ * This is an alias for the [plus] operator, providing a more explicit method name
132+ * for combining credentials.
133+ *
134+ * @param other The credential to combine with this one.
135+ * @return A new credential that applies both credentials.
136+ * @see plus
137+ */
21138public fun GrpcCallCredentials.combine (other : GrpcCallCredentials ): GrpcCallCredentials = this + other
22139
140+ /* *
141+ * A call credential that performs no authentication.
142+ *
143+ * This is useful as a no-op placeholder or for disabling authentication in specific scenarios.
144+ * Since it performs no authentication, it does not require transport security.
145+ *
146+ * ## Example
147+ *
148+ * ```kotlin
149+ * val credentials = if (useAuth) {
150+ * BearerTokenCredentials(token)
151+ * } else {
152+ * EmptyCallCredentials
153+ * }
154+ * ```
155+ */
23156public object EmptyCallCredentials : GrpcCallCredentials {
24157 override suspend fun GrpcMetadata.applyOnMetadata (callOptions : GrpcCallOptions ) {
25158 // do nothing
@@ -43,5 +176,4 @@ internal class CombinedCallCredentials(
43176 }
44177
45178 override val requiresTransportSecurity: Boolean = first.requiresTransportSecurity || second.requiresTransportSecurity
46-
47179}
0 commit comments