perxis.auth

Модуль для аутентификации в gRPC с использованием OAuth2 и API-ключей.

Этот модуль предоставляет два класса для аутентификации:

  • OAuth2Plugin: Аутентификация с помощью OAuth2.
  • APIKeyPlugin: Аутентификация с использованием API-ключа.

Данный код можно использовать для настройки gRPC-клиента с нужным типом аутентификации.

Пример использования:

gRPC Python предоставляет способ перехвата RPC и добавления метаданных, связанных с аутентификацией, через AuthMetadataPlugin. Те, кому нужен специальный метод аутентификации, могут просто предоставить конкретную реализацию следующего интерфейса:

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.
        """

Затем передайте экземпляр конкретной реализации в функцию grpc.metadata_call_credentials, которая будет преобразована в объект CallCredentials.

ОБРАТИТЕ ВНИМАНИЕ, что можно передать объект функции 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.
    """

Объект CallCredentials можно передать непосредственно в RPC, например:

>>> call_credentials = grpc.metadata_call_credentials(my_foo_plugin)
>>> stub.FooRpc(request, credentials=call_credentials)

Пример авторизации и аутентификации OAuth2

import grpc
from oauthlib.oauth2 import Client
from oauthlib.oauth2.rfc6749.parameters import prepare_token_request
from perxis.auth import OAuth2Plugin
from perxis.users.users_pb2 import GetRequest
from perxis.users.users_pb2_grpc import UsersStub


class ExtendedClient(Client):
    def __init__(self, client_id, grant_type, username, password, **kwargs):
        self.grant_type = grant_type
        self.username = username
        self.password = password
        super(ExtendedClient, self).__init__(client_id, **kwargs)

    def prepare_request_body(self, body='', scope=None, include_client_id=False, **kwargs):
        kwargs['client_id'] = self.client_id
        kwargs['include_client_id'] = include_client_id
        return prepare_token_request(
            self.grant_type, 
            body=body, 
            username=self.username,              
            password=self.password, 
            scope=scope, 
            **kwargs
        )

oauth2_plugin = OAuth2Plugin(
    client=ExtendedClient(
        client_id='client_id', 
        grant_type='password', 
        username='user', 
        password='pass'
    ), 
    client_secret='client_secret', 
    token_url='https://example.com/oauth/token', 
    audience='audience'
)

call_credentials = grpc.metadata_call_credentials(oauth2_plugin)

with grpc.insecure_channel('localhost:50051') as channel:
    request = GetRequest(user_id=1)
    stub = UsersStub(channel)
    stub.Get(request, credentials=call_credentials)

  1"""
  2Модуль для аутентификации в gRPC с использованием OAuth2 и API-ключей.
  3
  4Этот модуль предоставляет два класса для аутентификации:
  5
  6- [`OAuth2Plugin`](#OAuth2Plugin): Аутентификация с помощью OAuth2.
  7- [`APIKeyPlugin`](#APIKeyPlugin): Аутентификация с использованием API-ключа.
  8
  9Данный код можно использовать для настройки gRPC-клиента с нужным типом аутентификации.
 10
 11## Пример использования:
 12.. include:: ../README.md
 13   :start-line: 22
 14   :end-line: 120
 15---
 16"""
 17
 18import time
 19import grpc
 20from oauthlib.oauth2 import Client, OAuth2Token
 21from requests_oauthlib import OAuth2Session
 22
 23
 24class OAuth2Plugin(grpc.AuthMetadataPlugin):
 25    """Спецификация аутентификации для gRPC с использованием OAuth2.
 26
 27    Этот класс управляет получением и обновлением токена OAuth2 для аутентификации
 28    gRPC-запросов.
 29
 30    Attributes:
 31        _token (str | None): Токен. По умолчанию None.
 32        oauth2 (OAuth2Session): Сессия OAuth2 для управления токенами.
 33    Args:
 34        client (Client): OAuth2 клиент.
 35        client_secret (str): Секрет клиента.
 36        token_url (str): URL для получения токена.
 37        audience (str): Получатель (аудитория) токена, указывающая, для какого
 38                        сервиса он предназначен.
 39        signature_header_key (str, optional): Заголовок для подписи запроса.
 40                                              По умолчанию "authorization".
 41        token_type (str, optional): Тип токена. По умолчанию "Bearer".
 42    """
 43
 44    _token = None
 45
 46    def __init__(
 47        self,
 48        client: Client,
 49        client_secret: str,
 50        token_url: str,
 51        audience: str,
 52        signature_header_key: str = "authorization",
 53        token_type: str = "Bearer",
 54    ) -> None:
 55        """Инициализирует объект аутентификации OAuth2 для gRPC.
 56
 57        Этот метод устанавливает основные параметры OAuth2-клиента, необходимые
 58        для получения и обновления токена.
 59
 60        Args:
 61            client (Client): OAuth2 клиент для аутентификации.
 62            client_secret (str): Секретный ключ клиента.
 63            token_url (str): URL-адрес сервера авторизации для получения токена.
 64            audience (str): Аудитория токена, указывающая целевой сервис.
 65            signature_header_key (str, optional): Заголовок, в который будет добавляться токен.
 66                                                  По умолчанию "authorization".
 67            token_type (str, optional): Тип токена. По умолчанию "Bearer".
 68        """
 69        self._client_secret = client_secret
 70        self._token_url = token_url
 71        self._audience = audience
 72        self._signature_header_key = signature_header_key
 73        self._token_type = token_type
 74        self.oauth2 = OAuth2Session(client=client)
 75
 76    def __call__(
 77        self,
 78        context: grpc.AuthMetadataContext,
 79        callback: grpc.AuthMetadataPluginCallback,
 80    ) -> None:
 81        """Добавляет токен в метаданные gRPC-запроса.
 82
 83        Args:
 84            context (grpc.AuthMetadataContext): Контекст аутентификации gRPC.
 85            callback (grpc.AuthMetadataPluginCallback): Callback для передачи метаданных.
 86        """
 87        callback(
 88            metadata=(
 89                (
 90                    self._signature_header_key,
 91                    f'{self._token_type} {self.token["access_token"]}',
 92                ),
 93            ),
 94            error=None,
 95        )
 96
 97    @property
 98    def token(self) -> OAuth2Token:
 99        """Получает текущий OAuth2-токен, обновляя его при необходимости.
100
101        Returns:
102            OAuth2Token: Актуальный токен.
103        """
104
105        def fetch_token() -> OAuth2Token:
106            return self.oauth2.fetch_token(
107                token_url=self._token_url,
108                client_secret=self._client_secret,
109                audience=self._audience,
110            )
111
112        def refresh_token() -> OAuth2Token:
113            return self.oauth2.refresh_token(
114                token_url=self._token_url,
115                client_secret=self._client_secret,
116                audience=self._audience,
117            )
118
119        if self._token is None:
120            self._token = fetch_token()
121
122        if self._token["expires_at"] and self._token["expires_at"] < time.time():
123            if "refresh_token" in self._token:
124                self._token = refresh_token()
125            else:
126                self._token = fetch_token()
127        return self._token
128
129
130class APIKeyPlugin(grpc.AuthMetadataPlugin):
131    """Спецификация аутентификации для gRPC с использованием API-ключа.
132
133    Этот класс добавляет API-ключ в заголовок запроса для аутентификации
134    gRPC-запросов.
135
136    Attributes:
137        _token (str): API-ключ.
138        _signature_header_key (str): Заголовок, в который вставляется ключ.
139        _token_type (str): Тип токена (например, "API-Key").
140    Args:
141        token (str): API-ключ.
142        signature_header_key (str, optional): Заголовок для подписи запроса.
143                                              По умолчанию "authorization".
144        token_type (str, optional): Тип токена. По умолчанию "API-Key".
145    """
146
147    _token = None
148
149    def __init__(
150        self,
151        token: str,
152        signature_header_key: str = "authorization",
153        token_type: str = "API-Key",
154    ) -> None:
155        """Инициализирует объект аутентификации API-ключа для gRPC.
156
157        Args:
158            token (str): API-ключ, используемый для аутентификации.
159            signature_header_key (str, optional): Заголовок, в который будет добавляться API-ключ.
160                                                  По умолчанию "authorization".
161            token_type (str, optional): Тип токена. По умолчанию "API-Key".
162        """
163        self._token = token
164        self._signature_header_key = signature_header_key
165        self._token_type = token_type
166
167    def __call__(
168        self,
169        context: grpc.AuthMetadataContext,
170        callback: grpc.AuthMetadataPluginCallback,
171    ) -> None:
172        """Добавляет API-ключ в метаданные gRPC-запроса.
173
174        Args:
175            context (grpc.AuthMetadataContext): Контекст аутентификации gRPC.
176            callback (grpc.AuthMetadataPluginCallback): Callback для передачи метаданных.
177        """
178        callback(
179            metadata=(
180                (self._signature_header_key, f"{self._token_type} {self._token}"),
181            ),
182            error=None,
183        )
class OAuth2Plugin(grpc.AuthMetadataPlugin):
 25class OAuth2Plugin(grpc.AuthMetadataPlugin):
 26    """Спецификация аутентификации для gRPC с использованием OAuth2.
 27
 28    Этот класс управляет получением и обновлением токена OAuth2 для аутентификации
 29    gRPC-запросов.
 30
 31    Attributes:
 32        _token (str | None): Токен. По умолчанию None.
 33        oauth2 (OAuth2Session): Сессия OAuth2 для управления токенами.
 34    Args:
 35        client (Client): OAuth2 клиент.
 36        client_secret (str): Секрет клиента.
 37        token_url (str): URL для получения токена.
 38        audience (str): Получатель (аудитория) токена, указывающая, для какого
 39                        сервиса он предназначен.
 40        signature_header_key (str, optional): Заголовок для подписи запроса.
 41                                              По умолчанию "authorization".
 42        token_type (str, optional): Тип токена. По умолчанию "Bearer".
 43    """
 44
 45    _token = None
 46
 47    def __init__(
 48        self,
 49        client: Client,
 50        client_secret: str,
 51        token_url: str,
 52        audience: str,
 53        signature_header_key: str = "authorization",
 54        token_type: str = "Bearer",
 55    ) -> None:
 56        """Инициализирует объект аутентификации OAuth2 для gRPC.
 57
 58        Этот метод устанавливает основные параметры OAuth2-клиента, необходимые
 59        для получения и обновления токена.
 60
 61        Args:
 62            client (Client): OAuth2 клиент для аутентификации.
 63            client_secret (str): Секретный ключ клиента.
 64            token_url (str): URL-адрес сервера авторизации для получения токена.
 65            audience (str): Аудитория токена, указывающая целевой сервис.
 66            signature_header_key (str, optional): Заголовок, в который будет добавляться токен.
 67                                                  По умолчанию "authorization".
 68            token_type (str, optional): Тип токена. По умолчанию "Bearer".
 69        """
 70        self._client_secret = client_secret
 71        self._token_url = token_url
 72        self._audience = audience
 73        self._signature_header_key = signature_header_key
 74        self._token_type = token_type
 75        self.oauth2 = OAuth2Session(client=client)
 76
 77    def __call__(
 78        self,
 79        context: grpc.AuthMetadataContext,
 80        callback: grpc.AuthMetadataPluginCallback,
 81    ) -> None:
 82        """Добавляет токен в метаданные gRPC-запроса.
 83
 84        Args:
 85            context (grpc.AuthMetadataContext): Контекст аутентификации gRPC.
 86            callback (grpc.AuthMetadataPluginCallback): Callback для передачи метаданных.
 87        """
 88        callback(
 89            metadata=(
 90                (
 91                    self._signature_header_key,
 92                    f'{self._token_type} {self.token["access_token"]}',
 93                ),
 94            ),
 95            error=None,
 96        )
 97
 98    @property
 99    def token(self) -> OAuth2Token:
100        """Получает текущий OAuth2-токен, обновляя его при необходимости.
101
102        Returns:
103            OAuth2Token: Актуальный токен.
104        """
105
106        def fetch_token() -> OAuth2Token:
107            return self.oauth2.fetch_token(
108                token_url=self._token_url,
109                client_secret=self._client_secret,
110                audience=self._audience,
111            )
112
113        def refresh_token() -> OAuth2Token:
114            return self.oauth2.refresh_token(
115                token_url=self._token_url,
116                client_secret=self._client_secret,
117                audience=self._audience,
118            )
119
120        if self._token is None:
121            self._token = fetch_token()
122
123        if self._token["expires_at"] and self._token["expires_at"] < time.time():
124            if "refresh_token" in self._token:
125                self._token = refresh_token()
126            else:
127                self._token = fetch_token()
128        return self._token

Спецификация аутентификации для gRPC с использованием OAuth2.

Этот класс управляет получением и обновлением токена OAuth2 для аутентификации gRPC-запросов.

Attributes:
  • _token (str | None): Токен. По умолчанию None.
  • oauth2 (OAuth2Session): Сессия OAuth2 для управления токенами.
Arguments:
  • client (Client): OAuth2 клиент.
  • client_secret (str): Секрет клиента.
  • token_url (str): URL для получения токена.
  • audience (str): Получатель (аудитория) токена, указывающая, для какого сервиса он предназначен.
  • signature_header_key (str, optional): Заголовок для подписи запроса. По умолчанию "authorization".
  • token_type (str, optional): Тип токена. По умолчанию "Bearer".
OAuth2Plugin( client: oauthlib.oauth2.rfc6749.clients.base.Client, client_secret: str, token_url: str, audience: str, signature_header_key: str = 'authorization', token_type: str = 'Bearer')
47    def __init__(
48        self,
49        client: Client,
50        client_secret: str,
51        token_url: str,
52        audience: str,
53        signature_header_key: str = "authorization",
54        token_type: str = "Bearer",
55    ) -> None:
56        """Инициализирует объект аутентификации OAuth2 для gRPC.
57
58        Этот метод устанавливает основные параметры OAuth2-клиента, необходимые
59        для получения и обновления токена.
60
61        Args:
62            client (Client): OAuth2 клиент для аутентификации.
63            client_secret (str): Секретный ключ клиента.
64            token_url (str): URL-адрес сервера авторизации для получения токена.
65            audience (str): Аудитория токена, указывающая целевой сервис.
66            signature_header_key (str, optional): Заголовок, в который будет добавляться токен.
67                                                  По умолчанию "authorization".
68            token_type (str, optional): Тип токена. По умолчанию "Bearer".
69        """
70        self._client_secret = client_secret
71        self._token_url = token_url
72        self._audience = audience
73        self._signature_header_key = signature_header_key
74        self._token_type = token_type
75        self.oauth2 = OAuth2Session(client=client)

Инициализирует объект аутентификации OAuth2 для gRPC.

Этот метод устанавливает основные параметры OAuth2-клиента, необходимые для получения и обновления токена.

Arguments:
  • client (Client): OAuth2 клиент для аутентификации.
  • client_secret (str): Секретный ключ клиента.
  • token_url (str): URL-адрес сервера авторизации для получения токена.
  • audience (str): Аудитория токена, указывающая целевой сервис.
  • signature_header_key (str, optional): Заголовок, в который будет добавляться токен. По умолчанию "authorization".
  • token_type (str, optional): Тип токена. По умолчанию "Bearer".
oauth2
token: oauthlib.oauth2.rfc6749.tokens.OAuth2Token
 98    @property
 99    def token(self) -> OAuth2Token:
100        """Получает текущий OAuth2-токен, обновляя его при необходимости.
101
102        Returns:
103            OAuth2Token: Актуальный токен.
104        """
105
106        def fetch_token() -> OAuth2Token:
107            return self.oauth2.fetch_token(
108                token_url=self._token_url,
109                client_secret=self._client_secret,
110                audience=self._audience,
111            )
112
113        def refresh_token() -> OAuth2Token:
114            return self.oauth2.refresh_token(
115                token_url=self._token_url,
116                client_secret=self._client_secret,
117                audience=self._audience,
118            )
119
120        if self._token is None:
121            self._token = fetch_token()
122
123        if self._token["expires_at"] and self._token["expires_at"] < time.time():
124            if "refresh_token" in self._token:
125                self._token = refresh_token()
126            else:
127                self._token = fetch_token()
128        return self._token

Получает текущий OAuth2-токен, обновляя его при необходимости.

Returns:

OAuth2Token: Актуальный токен.

class APIKeyPlugin(grpc.AuthMetadataPlugin):
131class APIKeyPlugin(grpc.AuthMetadataPlugin):
132    """Спецификация аутентификации для gRPC с использованием API-ключа.
133
134    Этот класс добавляет API-ключ в заголовок запроса для аутентификации
135    gRPC-запросов.
136
137    Attributes:
138        _token (str): API-ключ.
139        _signature_header_key (str): Заголовок, в который вставляется ключ.
140        _token_type (str): Тип токена (например, "API-Key").
141    Args:
142        token (str): API-ключ.
143        signature_header_key (str, optional): Заголовок для подписи запроса.
144                                              По умолчанию "authorization".
145        token_type (str, optional): Тип токена. По умолчанию "API-Key".
146    """
147
148    _token = None
149
150    def __init__(
151        self,
152        token: str,
153        signature_header_key: str = "authorization",
154        token_type: str = "API-Key",
155    ) -> None:
156        """Инициализирует объект аутентификации API-ключа для gRPC.
157
158        Args:
159            token (str): API-ключ, используемый для аутентификации.
160            signature_header_key (str, optional): Заголовок, в который будет добавляться API-ключ.
161                                                  По умолчанию "authorization".
162            token_type (str, optional): Тип токена. По умолчанию "API-Key".
163        """
164        self._token = token
165        self._signature_header_key = signature_header_key
166        self._token_type = token_type
167
168    def __call__(
169        self,
170        context: grpc.AuthMetadataContext,
171        callback: grpc.AuthMetadataPluginCallback,
172    ) -> None:
173        """Добавляет API-ключ в метаданные gRPC-запроса.
174
175        Args:
176            context (grpc.AuthMetadataContext): Контекст аутентификации gRPC.
177            callback (grpc.AuthMetadataPluginCallback): Callback для передачи метаданных.
178        """
179        callback(
180            metadata=(
181                (self._signature_header_key, f"{self._token_type} {self._token}"),
182            ),
183            error=None,
184        )

Спецификация аутентификации для gRPC с использованием API-ключа.

Этот класс добавляет API-ключ в заголовок запроса для аутентификации gRPC-запросов.

Attributes:
  • _token (str): API-ключ.
  • _signature_header_key (str): Заголовок, в который вставляется ключ.
  • _token_type (str): Тип токена (например, "API-Key").
Arguments:
  • token (str): API-ключ.
  • signature_header_key (str, optional): Заголовок для подписи запроса. По умолчанию "authorization".
  • token_type (str, optional): Тип токена. По умолчанию "API-Key".
APIKeyPlugin( token: str, signature_header_key: str = 'authorization', token_type: str = 'API-Key')
150    def __init__(
151        self,
152        token: str,
153        signature_header_key: str = "authorization",
154        token_type: str = "API-Key",
155    ) -> None:
156        """Инициализирует объект аутентификации API-ключа для gRPC.
157
158        Args:
159            token (str): API-ключ, используемый для аутентификации.
160            signature_header_key (str, optional): Заголовок, в который будет добавляться API-ключ.
161                                                  По умолчанию "authorization".
162            token_type (str, optional): Тип токена. По умолчанию "API-Key".
163        """
164        self._token = token
165        self._signature_header_key = signature_header_key
166        self._token_type = token_type

Инициализирует объект аутентификации API-ключа для gRPC.

Arguments:
  • token (str): API-ключ, используемый для аутентификации.
  • signature_header_key (str, optional): Заголовок, в который будет добавляться API-ключ. По умолчанию "authorization".
  • token_type (str, optional): Тип токена. По умолчанию "API-Key".