|
|
|
# Authentication Extension Example in gRPC Python
|
|
|
|
|
|
|
|
## Check Our Guide First
|
|
|
|
|
|
|
|
For most common usage of authentication in gRPC Python, please see our
|
|
|
|
[Authentication](https://grpc.io/docs/guides/auth/) guide's Python section. The
|
|
|
|
Guide includes following scenarios:
|
|
|
|
|
|
|
|
1. Server SSL credential setup
|
|
|
|
2. Client SSL credential setup
|
|
|
|
3. Authenticate with Google using a JWT
|
|
|
|
4. Authenticate with Google using an Oauth2 token
|
|
|
|
|
|
|
|
Also, the guide talks about gRPC specific credential types.
|
|
|
|
|
|
|
|
### Channel credentials
|
|
|
|
|
|
|
|
Channel credentials are attached to a `Channel` object, the most common use case
|
|
|
|
are SSL credentials.
|
|
|
|
|
|
|
|
### Call credentials
|
|
|
|
|
|
|
|
Call credentials are attached to a `Call` object (corresponding to an RPC).
|
|
|
|
Under the hood, the call credentials is a function that takes in information of
|
|
|
|
the RPC and modify metadata through callback.
|
|
|
|
|
|
|
|
## About This Example
|
|
|
|
|
|
|
|
This example focuses on extending gRPC authentication mechanism:
|
|
|
|
1) Customize authentication plugin;
|
|
|
|
2) Composite client side credentials;
|
|
|
|
3) Validation through interceptor on server side.
|
|
|
|
|
|
|
|
## AuthMetadataPlugin: Manipulate metadata for each call
|
|
|
|
|
|
|
|
Unlike TLS/SSL based authentication, the authentication extension in gRPC Python
|
|
|
|
lives at a much higher level of networking. It relies on the transmission of
|
|
|
|
metadata (HTTP Header) between client and server, instead of alternating the
|
|
|
|
transport protocol.
|
|
|
|
|
|
|
|
gRPC Python provides a way to intercept an RPC and append authentication related
|
|
|
|
metadata through
|
|
|
|
[`AuthMetadataPlugin`](https://grpc.github.io/grpc/python/grpc.html#grpc.AuthMetadataPlugin).
|
|
|
|
Those in need of a custom authentication method may simply provide a concrete
|
|
|
|
implementation of the following interface:
|
|
|
|
|
|
|
|
```Python
|
|
|
|
class AuthMetadataPlugin:
|
|
|
|
"""A specification for custom authentication."""
|
|
|
|
|
|
|
|
def __call__(self, context, callback):
|
|
|
|
"""Implements authentication by passing metadata to a callback.
|
|
|
|
|
|
|
|
Implementations of this method must not block.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
context: An AuthMetadataContext providing information on the RPC that
|
|
|
|
the plugin is being called to authenticate.
|
|
|
|
callback: An AuthMetadataPluginCallback to be invoked either
|
|
|
|
synchronously or asynchronously.
|
|
|
|
"""
|
|
|
|
```
|
|
|
|
|
|
|
|
Then pass the instance of the concrete implementation to
|
|
|
|
`grpc.metadata_call_credentials` function to be converted into a
|
|
|
|
`CallCredentials` object. Please NOTE that it is possible to pass a Python
|
|
|
|
function object directly, but we recommend to inherit from the base class to
|
|
|
|
ensure implementation correctness.
|
|
|
|
|
|
|
|
|
|
|
|
```Python
|
|
|
|
def metadata_call_credentials(metadata_plugin, name=None):
|
|
|
|
"""Construct CallCredentials from an AuthMetadataPlugin.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
metadata_plugin: An AuthMetadataPlugin to use for authentication.
|
|
|
|
name: An optional name for the plugin.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A CallCredentials.
|
|
|
|
"""
|
|
|
|
```
|
|
|
|
|
|
|
|
The `CallCredentials` object can be passed directly into an RPC like:
|
|
|
|
|
|
|
|
```Python
|
|
|
|
call_credentials = grpc.metadata_call_credentials(my_foo_plugin)
|
|
|
|
stub.FooRpc(request, credentials=call_credentials)
|
|
|
|
```
|
|
|
|
|
|
|
|
Or you can use `ChannelCredentials` and `CallCredentials` at the same time by
|
|
|
|
combining them:
|
|
|
|
|
|
|
|
```Python
|
|
|
|
channel_credentials = ...
|
|
|
|
call_credentials = ...
|
|
|
|
composite_credentials = grpc.composite_channel_credentials(
|
|
|
|
channel_credential,
|
|
|
|
call_credentials)
|
|
|
|
channel = grpc.secure_channel(server_address, composite_credentials)
|
|
|
|
```
|
|
|
|
|
|
|
|
It is also possible to apply multiple `CallCredentials` to a single RPC:
|
|
|
|
|
|
|
|
```Python
|
|
|
|
call_credentials_foo = ...
|
|
|
|
call_credentials_bar = ...
|
|
|
|
call_credentials = grpc.composite_call_credentials(
|
|
|
|
call_credentials_foo,
|
|
|
|
call_credentials_bar)
|
|
|
|
stub.FooRpc(request, credentials=call_credentials)
|
|
|
|
```
|