| # 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) |
| ``` |
| |
| ## Token-based authentication |
| |
| Instead of `AuthMetadataPlugin`, you can also use token-based authentication |
| mechanisms using OAuth2 tokens or other customized tokens. |
| |
| OAuth2 tokens can be obtained using libraries like [google-auth](https://google-auth.readthedocs.io/en/master/user-guide.html): |
| |
| ```Python |
| import google.auth |
| |
| google_credentials, unused_project_id = google.auth.default() |
| call_credentials = grpc.access_token_call_credentials(google_credentials.token) |
| ``` |
| |
| After obtaining the token, the rest of the flow is documented in [token_based_auth_client.py](https://github.com/grpc/grpc/tree/master/examples/python/auth/token_based_auth_client.py) and [token_based_auth_server.py](https://github.com/grpc/grpc/tree/master/examples/python/auth/token_based_auth_server.py). |