kiarina-lib-google-auth 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,58 @@
1
+ import logging
2
+ from importlib import import_module
3
+ from importlib.metadata import version
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from ._helpers.get_credentials import get_credentials
8
+ from ._helpers.get_self_signed_jwt import get_self_signed_jwt
9
+ from ._types.credentials import Credentials
10
+ from ._types.credentials_cache import CredentialsCache
11
+ from ._utils.get_default_credentials import get_default_credentials
12
+ from ._utils.get_service_account_credentials import get_service_account_credentials
13
+ from ._utils.get_user_account_credentials import get_user_account_credentials
14
+ from .settings import GoogleAuthSettings, settings_manager
15
+
16
+ __version__ = version("kiarina-lib-google-auth")
17
+
18
+ __all__ = [
19
+ # ._helpers
20
+ "get_credentials",
21
+ "get_self_signed_jwt",
22
+ # ._types
23
+ "Credentials",
24
+ "CredentialsCache",
25
+ # ._utils
26
+ "get_default_credentials",
27
+ "get_service_account_credentials",
28
+ "get_user_account_credentials",
29
+ # .settings
30
+ "GoogleAuthSettings",
31
+ "settings_manager",
32
+ ]
33
+
34
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
35
+
36
+
37
+ def __getattr__(name: str) -> object:
38
+ if name not in __all__:
39
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
40
+
41
+ module_map = {
42
+ # ._helpers
43
+ "get_credentials": "._helpers.get_credentials",
44
+ "get_self_signed_jwt": "._helpers.get_self_signed_jwt",
45
+ # ._types
46
+ "Credentials": "._types.credentials",
47
+ "CredentialsCache": "._types.credentials_cache",
48
+ # ._utils
49
+ "get_default_credentials": "._utils.get_default_credentials",
50
+ "get_service_account_credentials": "._utils.get_service_account_credentials",
51
+ "get_user_account_credentials": "._utils.get_user_account_credentials",
52
+ # .settings
53
+ "GoogleAuthSettings": ".settings",
54
+ "settings_manager": ".settings",
55
+ }
56
+
57
+ globals()[name] = getattr(import_module(module_map[name], __name__), name)
58
+ return globals()[name]
File without changes
@@ -0,0 +1,63 @@
1
+ import google.auth.compute_engine.credentials
2
+ import google.oauth2.credentials
3
+ import google.oauth2.service_account
4
+ from google.auth import impersonated_credentials
5
+
6
+ from .._types.credentials import Credentials
7
+ from .._types.credentials_cache import CredentialsCache
8
+ from .._utils.get_default_credentials import get_default_credentials
9
+ from .._utils.get_service_account_credentials import get_service_account_credentials
10
+ from .._utils.get_user_account_credentials import get_user_account_credentials
11
+ from ..settings import GoogleAuthSettings, settings_manager
12
+
13
+
14
+ def get_credentials(
15
+ config_key: str | None = None,
16
+ *,
17
+ settings: GoogleAuthSettings | None = None,
18
+ scopes: list[str] | None = None,
19
+ cache: CredentialsCache | None = None,
20
+ ) -> Credentials:
21
+ if settings is None:
22
+ settings = settings_manager.get_settings_by_key(config_key)
23
+
24
+ credentials: Credentials
25
+
26
+ if settings.type == "default":
27
+ credentials = get_default_credentials()
28
+
29
+ elif settings.type == "service_account":
30
+ credentials = get_service_account_credentials(
31
+ service_account_file=settings.service_account_file,
32
+ service_account_data=settings.get_service_account_data(),
33
+ )
34
+
35
+ elif settings.type == "user_account":
36
+ credentials = get_user_account_credentials(
37
+ authorized_user_file=settings.authorized_user_file,
38
+ authorized_user_data=settings.get_authorized_user_data(),
39
+ scopes=scopes or settings.scopes,
40
+ cache=cache,
41
+ )
42
+
43
+ else:
44
+ raise ValueError(f"Unsupported credentials type: {settings.type}")
45
+
46
+ if settings.impersonate_service_account:
47
+ credentials = impersonated_credentials.Credentials( # type: ignore[no-untyped-call]
48
+ source_credentials=credentials,
49
+ target_principal=settings.impersonate_service_account,
50
+ target_scopes=scopes or settings.scopes,
51
+ )
52
+
53
+ assert isinstance(
54
+ credentials,
55
+ (
56
+ google.auth.compute_engine.credentials.Credentials,
57
+ google.oauth2.service_account.Credentials,
58
+ google.oauth2.credentials.Credentials,
59
+ impersonated_credentials.Credentials,
60
+ ),
61
+ ), f"Invalid credentials type: {type(credentials)}"
62
+
63
+ return credentials
@@ -0,0 +1,23 @@
1
+ from google.auth import jwt
2
+ from google.auth.transport.requests import Request
3
+
4
+ from .get_credentials import get_credentials
5
+ from ..settings import GoogleAuthSettings
6
+
7
+
8
+ def get_self_signed_jwt(
9
+ config_key: str | None = None,
10
+ *,
11
+ settings: GoogleAuthSettings | None = None,
12
+ audience: str,
13
+ ) -> str:
14
+ """
15
+ Get a self-signed JWT
16
+ """
17
+ credentials = get_credentials(config_key, settings=settings)
18
+
19
+ jwt_creds = jwt.Credentials.from_signing_credentials(credentials, audience=audience) # type: ignore[no-untyped-call]
20
+ # Generate a self-signed JWT. Does not communicate over the network
21
+ jwt_creds.refresh(Request()) # type: ignore[no-untyped-call]
22
+
23
+ return jwt_creds.token.decode("utf-8") # type: ignore[no-any-return]
File without changes
@@ -0,0 +1,13 @@
1
+ from typing import TypeAlias
2
+
3
+ import google.auth.compute_engine.credentials
4
+ import google.oauth2.credentials
5
+ import google.oauth2.service_account
6
+ from google.auth import impersonated_credentials
7
+
8
+ Credentials: TypeAlias = (
9
+ google.auth.compute_engine.credentials.Credentials
10
+ | google.oauth2.service_account.Credentials
11
+ | google.oauth2.credentials.Credentials
12
+ | impersonated_credentials.Credentials
13
+ )
@@ -0,0 +1,21 @@
1
+ from typing import Protocol
2
+
3
+
4
+ class CredentialsCache(Protocol):
5
+ """Protocol for a credentials cache."""
6
+
7
+ def get(self) -> str | None:
8
+ """Retrieve cached credentials.
9
+
10
+ Returns:
11
+ The cached credentials as a JSON string, or None if not found.
12
+ """
13
+ ...
14
+
15
+ def set(self, value: str) -> None:
16
+ """Store credentials in the cache.
17
+
18
+ Args:
19
+ value: The credentials to store as a JSON string.
20
+ """
21
+ ...
File without changes
@@ -0,0 +1,35 @@
1
+ import google.auth.compute_engine.credentials
2
+ import google.oauth2.credentials
3
+ import google.oauth2.service_account
4
+ from google.auth import default
5
+
6
+
7
+ def get_default_credentials() -> (
8
+ google.auth.compute_engine.credentials.Credentials
9
+ | google.oauth2.credentials.Credentials
10
+ | google.oauth2.service_account.Credentials
11
+ ):
12
+ """
13
+ Get default Google credentials
14
+
15
+ Default credentials are determined in the following priority order:
16
+
17
+ - `google.oauth2.service_account.Credentials`
18
+ - When the GOOGLE_APPLICATION_CREDENTIALS environment variable is set
19
+ - `google.oauth2.credentials.Credentials`
20
+ - When credentials set by the gcloud auth application-default login command exist
21
+ - `google.auth.compute_engine.credentials.Credentials`
22
+ - When running on GCP and the metadata server is available
23
+ """
24
+ credentials, _ = default() # type: ignore[no-untyped-call]
25
+
26
+ assert isinstance(
27
+ credentials,
28
+ (
29
+ google.auth.compute_engine.credentials.Credentials,
30
+ google.oauth2.credentials.Credentials,
31
+ google.oauth2.service_account.Credentials,
32
+ ),
33
+ ), f"Invalid credentials type: {type(credentials)}"
34
+
35
+ return credentials
@@ -0,0 +1,27 @@
1
+ import os
2
+
3
+ from google.oauth2.service_account import Credentials
4
+
5
+
6
+ def get_service_account_credentials(
7
+ *,
8
+ service_account_file: str | os.PathLike[str] | None = None,
9
+ service_account_data: dict[str, object] | None = None,
10
+ ) -> Credentials:
11
+ if service_account_data:
12
+ return Credentials.from_service_account_info(service_account_data) # type: ignore[no-untyped-call, no-any-return]
13
+
14
+ elif service_account_file:
15
+ service_account_file = os.path.expanduser(
16
+ os.path.expandvars(os.fspath(service_account_file))
17
+ )
18
+
19
+ if not os.path.exists(service_account_file):
20
+ raise ValueError(
21
+ f"Service account file does not exist: {service_account_file}"
22
+ )
23
+
24
+ return Credentials.from_service_account_file(service_account_file) # type: ignore[no-untyped-call, no-any-return]
25
+
26
+ else:
27
+ raise ValueError("No valid service account credentials found.")
@@ -0,0 +1,81 @@
1
+ import json
2
+ import os
3
+
4
+ from google.oauth2.credentials import Credentials
5
+ from google.auth.transport.requests import Request
6
+
7
+ from .._types.credentials_cache import CredentialsCache
8
+
9
+
10
+ def get_user_account_credentials(
11
+ *,
12
+ authorized_user_file: str | os.PathLike[str] | None = None,
13
+ authorized_user_data: dict[str, object] | None = None,
14
+ scopes: list[str],
15
+ cache: CredentialsCache | None = None,
16
+ ) -> Credentials:
17
+ if authorized_user_data:
18
+ credentials = Credentials.from_authorized_user_info( # type: ignore[no-untyped-call]
19
+ authorized_user_data,
20
+ scopes=scopes,
21
+ )
22
+
23
+ return _check_and_refresh_credentials(
24
+ credentials,
25
+ scopes=scopes,
26
+ cache=cache,
27
+ )
28
+
29
+ elif authorized_user_file:
30
+ authorized_user_file = os.path.expanduser(
31
+ os.path.expandvars(os.fspath(authorized_user_file))
32
+ )
33
+
34
+ if not os.path.exists(authorized_user_file):
35
+ raise ValueError(
36
+ f"Authorized user file does not exist: {authorized_user_file}"
37
+ )
38
+
39
+ credentials = Credentials.from_authorized_user_file( # type: ignore[no-untyped-call]
40
+ authorized_user_file,
41
+ scopes=scopes,
42
+ )
43
+
44
+ return _check_and_refresh_credentials(
45
+ credentials,
46
+ scopes=scopes,
47
+ cache=cache,
48
+ )
49
+
50
+ else:
51
+ raise ValueError("No valid user account credentials found.")
52
+
53
+
54
+ def _check_and_refresh_credentials(
55
+ credentials: Credentials,
56
+ *,
57
+ scopes: list[str],
58
+ cache: CredentialsCache | None = None,
59
+ ) -> Credentials:
60
+ if credentials.valid:
61
+ return credentials
62
+
63
+ if cache:
64
+ if cached_json_str := cache.get():
65
+ cached_credentials = Credentials.from_authorized_user_info( # type: ignore[no-untyped-call]
66
+ json.loads(cached_json_str),
67
+ scopes=scopes,
68
+ )
69
+
70
+ if cached_credentials.valid:
71
+ return cached_credentials # type: ignore[no-any-return]
72
+
73
+ credentials = cached_credentials
74
+
75
+ if credentials.expired and credentials.refresh_token:
76
+ credentials.refresh(Request()) # type: ignore[no-untyped-call]
77
+
78
+ if cache:
79
+ cache.set(credentials.to_json()) # type: ignore[no-untyped-call]
80
+
81
+ return credentials
File without changes
@@ -0,0 +1,116 @@
1
+ import json
2
+ import os
3
+ from typing import Any, Literal
4
+
5
+ from pydantic import Field, SecretStr, field_validator
6
+ from pydantic_settings import BaseSettings, SettingsConfigDict
7
+ from pydantic_settings_manager import SettingsManager
8
+
9
+
10
+ class GoogleAuthSettings(BaseSettings):
11
+ model_config = SettingsConfigDict(env_prefix="KIARINA_LIB_GOOGLE_AUTH_")
12
+
13
+ type: Literal["default", "service_account", "user_account"] = "default"
14
+
15
+ # --------------------------------------------------
16
+ # Fields (service_account)
17
+ # --------------------------------------------------
18
+
19
+ service_account_email: str | None = None
20
+
21
+ service_account_file: str | None = None
22
+ """Path to the service account key file"""
23
+
24
+ service_account_data: SecretStr | None = None
25
+ """Service account key data in JSON format"""
26
+
27
+ # --------------------------------------------------
28
+ # Fields (user_account)
29
+ # --------------------------------------------------
30
+
31
+ user_account_email: str | None = None
32
+
33
+ client_secret_file: str | None = None
34
+ """Path to the client secret file"""
35
+
36
+ client_secret_data: SecretStr | None = None
37
+ """Client secret data in JSON format"""
38
+
39
+ authorized_user_file: str | None = None
40
+ """Path to the authorized user file"""
41
+
42
+ authorized_user_data: SecretStr | None = None
43
+ """
44
+ Authorized user data in JSON format
45
+
46
+ If expired, retrieve from cache.
47
+ If cache is not available, use for refresh.
48
+ Refreshed credentials will be cached.
49
+ """
50
+
51
+ # --------------------------------------------------
52
+ # Fields (common)
53
+ # --------------------------------------------------
54
+
55
+ impersonate_service_account: str | None = None
56
+ """
57
+ Email address of the service account to impersonate
58
+
59
+ The source principal requires the roles/iam.serviceAccountTokenCreator role.
60
+ Note that this required permission is not included in the roles/owner role.
61
+ """
62
+
63
+ scopes: list[str] = Field(
64
+ default_factory=lambda: [
65
+ "https://www.googleapis.com/auth/cloud-platform", # All GCP resources
66
+ "https://www.googleapis.com/auth/drive", # Google Drive resources
67
+ "https://www.googleapis.com/auth/spreadsheets", # Google Sheets resources
68
+ ]
69
+ )
70
+ """
71
+ List of scopes to request during the authentication
72
+
73
+ Specify the scopes required for impersonation authentication.
74
+ Specify the scopes required for user account authentication.
75
+ """
76
+
77
+ project_id: str | None = None
78
+
79
+ # --------------------------------------------------
80
+ # Validators
81
+ # --------------------------------------------------
82
+
83
+ @field_validator(
84
+ "service_account_file",
85
+ "client_secret_file",
86
+ "authorized_user_file",
87
+ mode="before",
88
+ )
89
+ @classmethod
90
+ def expand_user(cls, v: str | None) -> str | None:
91
+ return os.path.expanduser(v) if isinstance(v, str) else v
92
+
93
+ # --------------------------------------------------
94
+ # Methods
95
+ # --------------------------------------------------
96
+
97
+ def get_service_account_data(self) -> dict[str, Any] | None:
98
+ if not self.service_account_data:
99
+ return None
100
+
101
+ return json.loads(self.service_account_data.get_secret_value()) # type: ignore[no-any-return]
102
+
103
+ def get_client_secret_data(self) -> dict[str, Any] | None:
104
+ if not self.client_secret_data:
105
+ return None
106
+
107
+ return json.loads(self.client_secret_data.get_secret_value()) # type: ignore[no-any-return]
108
+
109
+ def get_authorized_user_data(self) -> dict[str, Any] | None:
110
+ if not self.authorized_user_data:
111
+ return None
112
+
113
+ return json.loads(self.authorized_user_data.get_secret_value()) # type: ignore[no-any-return]
114
+
115
+
116
+ settings_manager = SettingsManager(GoogleAuthSettings, multi=True)
@@ -0,0 +1,502 @@
1
+ Metadata-Version: 2.4
2
+ Name: kiarina-lib-google-auth
3
+ Version: 1.4.0
4
+ Summary: Google Cloud client library for kiarina namespace
5
+ Project-URL: Homepage, https://github.com/kiarina/kiarina-python
6
+ Project-URL: Repository, https://github.com/kiarina/kiarina-python
7
+ Project-URL: Issues, https://github.com/kiarina/kiarina-python/issues
8
+ Project-URL: Changelog, https://github.com/kiarina/kiarina-python/blob/main/packages/kiarina-lib-google-auth/CHANGELOG.md
9
+ Project-URL: Documentation, https://github.com/kiarina/kiarina-python/tree/main/packages/kiarina-lib-google-auth#readme
10
+ Author-email: kiarina <kiarinadawa@gmail.com>
11
+ Maintainer-email: kiarina <kiarinadawa@gmail.com>
12
+ License-Expression: MIT
13
+ Keywords: client,cloud,gcp,google,google-cloud,pydantic,settings
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.12
23
+ Requires-Dist: google-api-python-client>=2.184.0
24
+ Requires-Dist: pydantic-settings-manager>=2.1.0
25
+ Requires-Dist: pydantic-settings>=2.10.1
26
+ Description-Content-Type: text/markdown
27
+
28
+ # kiarina-lib-google-auth
29
+
30
+ A Python client library for Google Cloud authentication with configuration management and support for multiple credential types.
31
+
32
+ ## Features
33
+
34
+ - **Multiple Authentication Methods**: Support for default credentials, service accounts, and user accounts
35
+ - **Service Account Impersonation**: Impersonate service accounts for delegated access
36
+ - **Configuration Management**: Use `pydantic-settings-manager` for flexible configuration
37
+ - **Credentials Caching**: Cache and refresh user account credentials automatically
38
+ - **Self-Signed JWT**: Generate self-signed JWTs for service accounts
39
+ - **Type Safety**: Full type hints and Pydantic validation
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install kiarina-lib-google-auth
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ### Basic Usage with Default Credentials
50
+
51
+ ```python
52
+ from kiarina.lib.google.auth import get_credentials
53
+
54
+ # Get default credentials (ADC, service account, or compute engine)
55
+ credentials = get_credentials()
56
+ ```
57
+
58
+ ### Service Account Authentication
59
+
60
+ ```python
61
+ from kiarina.lib.google.auth import get_credentials, GoogleAuthSettings
62
+
63
+ # From service account key file
64
+ credentials = get_credentials(
65
+ settings=GoogleAuthSettings(
66
+ type="service_account",
67
+ service_account_file="~/path/to/service-account-key.json"
68
+ )
69
+ )
70
+
71
+ # From service account key data (JSON string)
72
+ import json
73
+ credentials = get_credentials(
74
+ settings=GoogleAuthSettings(
75
+ type="service_account",
76
+ service_account_data=json.dumps({
77
+ "type": "service_account",
78
+ "project_id": "your-project",
79
+ "private_key_id": "...",
80
+ "private_key": "...",
81
+ "client_email": "...",
82
+ # ... other fields
83
+ })
84
+ )
85
+ )
86
+ ```
87
+
88
+ ### User Account Authentication
89
+
90
+ ```python
91
+ from kiarina.lib.google.auth import get_credentials, GoogleAuthSettings
92
+
93
+ # From authorized user file (OAuth2 credentials)
94
+ credentials = get_credentials(
95
+ settings=GoogleAuthSettings(
96
+ type="user_account",
97
+ authorized_user_file="~/path/to/authorized-user.json",
98
+ scopes=["https://www.googleapis.com/auth/drive"]
99
+ )
100
+ )
101
+
102
+ # From authorized user data (JSON string)
103
+ credentials = get_credentials(
104
+ settings=GoogleAuthSettings(
105
+ type="user_account",
106
+ authorized_user_data=json.dumps({
107
+ "type": "authorized_user",
108
+ "client_id": "...",
109
+ "client_secret": "...",
110
+ "refresh_token": "..."
111
+ }),
112
+ scopes=["https://www.googleapis.com/auth/drive"]
113
+ )
114
+ )
115
+ ```
116
+
117
+ ### Service Account Impersonation
118
+
119
+ ```python
120
+ from kiarina.lib.google.auth import get_credentials, GoogleAuthSettings
121
+
122
+ # Impersonate a service account using source credentials
123
+ credentials = get_credentials(
124
+ settings=GoogleAuthSettings(
125
+ type="service_account",
126
+ service_account_file="~/path/to/source-sa-key.json",
127
+ impersonate_service_account="target-sa@project.iam.gserviceaccount.com",
128
+ scopes=["https://www.googleapis.com/auth/cloud-platform"]
129
+ )
130
+ )
131
+
132
+ # Note: Source principal requires roles/iam.serviceAccountTokenCreator role
133
+ ```
134
+
135
+ ### Credentials Caching
136
+
137
+ ```python
138
+ from kiarina.lib.google.auth import get_credentials, GoogleAuthSettings, CredentialsCache
139
+
140
+ # Implement a cache (e.g., in-memory, Redis, file-based)
141
+ class InMemoryCache(CredentialsCache):
142
+ def __init__(self):
143
+ self._cache: str | None = None
144
+
145
+ def get(self) -> str | None:
146
+ return self._cache
147
+
148
+ def set(self, value: str) -> None:
149
+ self._cache = value
150
+
151
+ cache = InMemoryCache()
152
+
153
+ # Use cache for user account credentials
154
+ credentials = get_credentials(
155
+ settings=GoogleAuthSettings(
156
+ type="user_account",
157
+ authorized_user_file="~/path/to/authorized-user.json",
158
+ scopes=["https://www.googleapis.com/auth/drive"]
159
+ ),
160
+ cache=cache
161
+ )
162
+ ```
163
+
164
+ ### Self-Signed JWT
165
+
166
+ ```python
167
+ from kiarina.lib.google.auth import get_self_signed_jwt, GoogleAuthSettings
168
+
169
+ # Generate a self-signed JWT for service account
170
+ jwt_token = get_self_signed_jwt(
171
+ settings=GoogleAuthSettings(
172
+ type="service_account",
173
+ service_account_file="~/path/to/service-account-key.json"
174
+ ),
175
+ audience="https://your-service.example.com/"
176
+ )
177
+ ```
178
+
179
+ ## Configuration
180
+
181
+ This library uses [pydantic-settings-manager](https://github.com/kiarina/pydantic-settings-manager) for flexible configuration management.
182
+
183
+ ### Environment Variables
184
+
185
+ Configure authentication using environment variables:
186
+
187
+ ```bash
188
+ # Authentication type
189
+ export KIARINA_LIB_GOOGLE_AUTH_TYPE="service_account"
190
+
191
+ # Service account configuration
192
+ export KIARINA_LIB_GOOGLE_AUTH_SERVICE_ACCOUNT_FILE="~/path/to/sa-key.json"
193
+ export KIARINA_LIB_GOOGLE_AUTH_SERVICE_ACCOUNT_EMAIL="sa@project.iam.gserviceaccount.com"
194
+
195
+ # User account configuration
196
+ export KIARINA_LIB_GOOGLE_AUTH_AUTHORIZED_USER_FILE="~/path/to/authorized-user.json"
197
+ export KIARINA_LIB_GOOGLE_AUTH_USER_ACCOUNT_EMAIL="user@example.com"
198
+
199
+ # Impersonation
200
+ export KIARINA_LIB_GOOGLE_AUTH_IMPERSONATE_SERVICE_ACCOUNT="target-sa@project.iam.gserviceaccount.com"
201
+
202
+ # Scopes (comma-separated)
203
+ export KIARINA_LIB_GOOGLE_AUTH_SCOPES="https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/drive"
204
+
205
+ # Project ID
206
+ export KIARINA_LIB_GOOGLE_AUTH_PROJECT_ID="your-project-id"
207
+ ```
208
+
209
+ ### Programmatic Configuration
210
+
211
+ ```python
212
+ from kiarina.lib.google.auth import settings_manager, get_credentials
213
+
214
+ # Configure multiple environments
215
+ settings_manager.user_config = {
216
+ "development": {
217
+ "type": "user_account",
218
+ "authorized_user_file": "~/.config/gcloud/application_default_credentials.json",
219
+ "scopes": ["https://www.googleapis.com/auth/cloud-platform"]
220
+ },
221
+ "production": {
222
+ "type": "service_account",
223
+ "service_account_file": "/secrets/prod-sa-key.json",
224
+ "scopes": ["https://www.googleapis.com/auth/cloud-platform"]
225
+ }
226
+ }
227
+
228
+ # Switch to production configuration
229
+ settings_manager.active_key = "production"
230
+ credentials = get_credentials()
231
+ ```
232
+
233
+ ## API Reference
234
+
235
+ ### Main Functions
236
+
237
+ #### `get_credentials(config_key=None, *, settings=None, scopes=None, cache=None)`
238
+
239
+ Get Google Cloud credentials based on configuration.
240
+
241
+ **Parameters:**
242
+ - `config_key` (str | None): Configuration key for multi-config setup
243
+ - `settings` (GoogleAuthSettings | None): Settings object (overrides config_key)
244
+ - `scopes` (list[str] | None): OAuth2 scopes (overrides settings.scopes)
245
+ - `cache` (CredentialsCache | None): Credentials cache for user accounts
246
+
247
+ **Returns:**
248
+ - `Credentials`: Google Cloud credentials object
249
+
250
+ **Supported credential types:**
251
+ - `google.auth.compute_engine.credentials.Credentials` (Compute Engine)
252
+ - `google.oauth2.service_account.Credentials` (Service Account)
253
+ - `google.oauth2.credentials.Credentials` (User Account)
254
+ - `google.auth.impersonated_credentials.Credentials` (Impersonated)
255
+
256
+ #### `get_self_signed_jwt(config_key=None, *, settings=None, audience)`
257
+
258
+ Generate a self-signed JWT for service account authentication.
259
+
260
+ **Parameters:**
261
+ - `config_key` (str | None): Configuration key for multi-config setup
262
+ - `settings` (GoogleAuthSettings | None): Settings object (overrides config_key)
263
+ - `audience` (str): JWT audience (target service URL)
264
+
265
+ **Returns:**
266
+ - `str`: Self-signed JWT token
267
+
268
+ ### Utility Functions
269
+
270
+ #### `get_default_credentials()`
271
+
272
+ Get default Google credentials using Application Default Credentials (ADC).
273
+
274
+ **Returns:**
275
+ - `Credentials`: Default credentials (ADC, service account, or compute engine)
276
+
277
+ #### `get_service_account_credentials(*, service_account_file=None, service_account_data=None)`
278
+
279
+ Get service account credentials from file or data.
280
+
281
+ **Parameters:**
282
+ - `service_account_file` (str | PathLike | None): Path to service account key file
283
+ - `service_account_data` (dict | None): Service account key data
284
+
285
+ **Returns:**
286
+ - `google.oauth2.service_account.Credentials`: Service account credentials
287
+
288
+ #### `get_user_account_credentials(*, authorized_user_file=None, authorized_user_data=None, scopes, cache=None)`
289
+
290
+ Get user account credentials from file or data.
291
+
292
+ **Parameters:**
293
+ - `authorized_user_file` (str | PathLike | None): Path to authorized user file
294
+ - `authorized_user_data` (dict | None): Authorized user data
295
+ - `scopes` (list[str]): OAuth2 scopes
296
+ - `cache` (CredentialsCache | None): Credentials cache
297
+
298
+ **Returns:**
299
+ - `google.oauth2.credentials.Credentials`: User account credentials
300
+
301
+ ### Configuration Classes
302
+
303
+ #### `GoogleAuthSettings`
304
+
305
+ Pydantic settings model for Google Cloud authentication.
306
+
307
+ **Fields:**
308
+ - `type` (Literal["default", "service_account", "user_account"]): Authentication type (default: "default")
309
+ - `service_account_email` (str | None): Service account email
310
+ - `service_account_file` (str | None): Path to service account key file
311
+ - `service_account_data` (str | None): Service account key data (JSON string)
312
+ - `user_account_email` (str | None): User account email
313
+ - `client_secret_file` (str | None): Path to client secret file
314
+ - `client_secret_data` (str | None): Client secret data (JSON string)
315
+ - `authorized_user_file` (str | None): Path to authorized user file
316
+ - `authorized_user_data` (str | None): Authorized user data (JSON string)
317
+ - `impersonate_service_account` (str | None): Target service account email for impersonation
318
+ - `scopes` (list[str]): OAuth2 scopes (default: cloud-platform, drive, spreadsheets)
319
+ - `project_id` (str | None): GCP project ID
320
+
321
+ **Methods:**
322
+ - `get_service_account_data()`: Parse service_account_data JSON string
323
+ - `get_client_secret_data()`: Parse client_secret_data JSON string
324
+ - `get_authorized_user_data()`: Parse authorized_user_data JSON string
325
+
326
+ #### `CredentialsCache` (Protocol)
327
+
328
+ Protocol for implementing credentials cache.
329
+
330
+ **Methods:**
331
+ - `get() -> str | None`: Retrieve cached credentials (JSON string)
332
+ - `set(value: str) -> None`: Store credentials in cache (JSON string)
333
+
334
+ ## Authentication Types
335
+
336
+ ### 1. Default Credentials
337
+
338
+ Uses Application Default Credentials (ADC) in the following priority:
339
+
340
+ 1. `GOOGLE_APPLICATION_CREDENTIALS` environment variable (service account key file)
341
+ 2. `gcloud auth application-default login` credentials (user account)
342
+ 3. Compute Engine metadata server (compute engine credentials)
343
+
344
+ ```python
345
+ credentials = get_credentials(
346
+ settings=GoogleAuthSettings(type="default")
347
+ )
348
+ ```
349
+
350
+ ### 2. Service Account
351
+
352
+ Authenticates using a service account key file or data.
353
+
354
+ ```python
355
+ # From file
356
+ credentials = get_credentials(
357
+ settings=GoogleAuthSettings(
358
+ type="service_account",
359
+ service_account_file="~/sa-key.json"
360
+ )
361
+ )
362
+
363
+ # From data
364
+ credentials = get_credentials(
365
+ settings=GoogleAuthSettings(
366
+ type="service_account",
367
+ service_account_data='{"type": "service_account", ...}'
368
+ )
369
+ )
370
+ ```
371
+
372
+ ### 3. User Account
373
+
374
+ Authenticates using OAuth2 user credentials (authorized user file).
375
+
376
+ ```python
377
+ credentials = get_credentials(
378
+ settings=GoogleAuthSettings(
379
+ type="user_account",
380
+ authorized_user_file="~/.config/gcloud/application_default_credentials.json",
381
+ scopes=["https://www.googleapis.com/auth/drive"]
382
+ )
383
+ )
384
+ ```
385
+
386
+ **Note:** User account credentials support automatic refresh and caching.
387
+
388
+ ### 4. Service Account Impersonation
389
+
390
+ Impersonate a target service account using source credentials.
391
+
392
+ ```python
393
+ credentials = get_credentials(
394
+ settings=GoogleAuthSettings(
395
+ type="service_account",
396
+ service_account_file="~/source-sa-key.json",
397
+ impersonate_service_account="target-sa@project.iam.gserviceaccount.com",
398
+ scopes=["https://www.googleapis.com/auth/cloud-platform"]
399
+ )
400
+ )
401
+ ```
402
+
403
+ **Required IAM Role:** The source principal must have the `roles/iam.serviceAccountTokenCreator` role on the target service account.
404
+
405
+ ## Default Scopes
406
+
407
+ The library includes the following default scopes:
408
+
409
+ - `https://www.googleapis.com/auth/cloud-platform` - All GCP resources
410
+ - `https://www.googleapis.com/auth/drive` - Google Drive resources
411
+ - `https://www.googleapis.com/auth/spreadsheets` - Google Sheets resources
412
+
413
+ You can override these by specifying custom scopes in the configuration or function call.
414
+
415
+ ## Error Handling
416
+
417
+ ```python
418
+ from kiarina.lib.google.auth import get_credentials, GoogleAuthSettings
419
+
420
+ try:
421
+ credentials = get_credentials(
422
+ settings=GoogleAuthSettings(
423
+ type="service_account",
424
+ service_account_file="~/sa-key.json"
425
+ )
426
+ )
427
+ except ValueError as e:
428
+ print(f"Configuration error: {e}")
429
+ except FileNotFoundError as e:
430
+ print(f"Key file not found: {e}")
431
+ except Exception as e:
432
+ print(f"Authentication failed: {e}")
433
+ ```
434
+
435
+ ## Development
436
+
437
+ ### Prerequisites
438
+
439
+ - Python 3.12+
440
+
441
+ ### Setup
442
+
443
+ ```bash
444
+ # Clone the repository
445
+ git clone https://github.com/kiarina/kiarina-python.git
446
+ cd kiarina-python
447
+
448
+ # Setup development environment
449
+ mise run setup
450
+ ```
451
+
452
+ ### Running Tests
453
+
454
+ ```bash
455
+ # Run format, lint, type checks and tests
456
+ mise run package kiarina-lib-google-auth
457
+
458
+ # Coverage report
459
+ mise run package:test kiarina-lib-google-auth --coverage
460
+ ```
461
+
462
+ ### Test Configuration
463
+
464
+ Some tests require actual GCP credentials. Set the following environment variables:
465
+
466
+ ```bash
467
+ # Service account key file
468
+ export KIARINA_LIB_GOOGLE_AUTH_TEST_GCP_SA_KEY_FILE="~/path/to/sa-key.json"
469
+
470
+ # Service account key data (JSON string)
471
+ export KIARINA_LIB_GOOGLE_AUTH_TEST_GCP_SA_KEY_DATA='{"type": "service_account", ...}'
472
+
473
+ # Authorized user file
474
+ export KIARINA_LIB_GOOGLE_AUTH_TEST_GCP_AUTHORIZED_USER_FILE="~/.config/gcloud/application_default_credentials.json"
475
+
476
+ # Authorized user data (JSON string)
477
+ export KIARINA_LIB_GOOGLE_AUTH_TEST_GCP_AUTHORIZED_USER_DATA='{"type": "authorized_user", ...}'
478
+
479
+ # Target service account for impersonation tests
480
+ export KIARINA_LIB_GOOGLE_AUTH_TEST_GCP_IMPERSONATE_SA="target-sa@project.iam.gserviceaccount.com"
481
+ ```
482
+
483
+ Tests will be skipped (xfail) if these environment variables are not set.
484
+
485
+ ## Dependencies
486
+
487
+ - [google-api-python-client](https://github.com/googleapis/google-api-python-client) - Google API client library
488
+ - [pydantic-settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) - Settings management
489
+ - [pydantic-settings-manager](https://github.com/kiarina/pydantic-settings-manager) - Advanced settings management
490
+
491
+ ## License
492
+
493
+ This project is licensed under the MIT License - see the [LICENSE](../../LICENSE) file for details.
494
+
495
+ ## Contributing
496
+
497
+ This is a personal project, but contributions are welcome! Please feel free to submit issues or pull requests.
498
+
499
+ ## Related Projects
500
+
501
+ - [kiarina-python](https://github.com/kiarina/kiarina-python) - The main monorepo containing this package
502
+ - [pydantic-settings-manager](https://github.com/kiarina/pydantic-settings-manager) - Configuration management library used by this package
@@ -0,0 +1,16 @@
1
+ kiarina/lib/google/auth/__init__.py,sha256=SdD3zDQ0MebC3qbtyjpZx3HZeUL5G-4fQ3mtqQT32us,2014
2
+ kiarina/lib/google/auth/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ kiarina/lib/google/auth/settings.py,sha256=kaOFOLl7xGQ6jOiBSjUZWLdWzj_Gic-lOAKZEE6yasg,3803
4
+ kiarina/lib/google/auth/_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ kiarina/lib/google/auth/_helpers/get_credentials.py,sha256=htDgNumKBwX3b11LHjRjSR-Ciou3NBWek8f7-caPy1g,2298
6
+ kiarina/lib/google/auth/_helpers/get_self_signed_jwt.py,sha256=rAFZXBk-xM5YNCiwi9bJzY4CxpR96NRUMJbCJ8bdH4k,756
7
+ kiarina/lib/google/auth/_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ kiarina/lib/google/auth/_types/credentials.py,sha256=8gjsjUydlbhW56JHfMHymbeoej5zp8ZopMQ2dYSvT70,415
9
+ kiarina/lib/google/auth/_types/credentials_cache.py,sha256=LrcJUAxEvDJoqqX0Gq84LKp6pJz1eiAg2_530SOZeuc,483
10
+ kiarina/lib/google/auth/_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ kiarina/lib/google/auth/_utils/get_default_credentials.py,sha256=2mkQ4eWM-lpM-1L7aprw9O2U3ZB-DE75cmpYj6d2fQs,1230
12
+ kiarina/lib/google/auth/_utils/get_service_account_credentials.py,sha256=XGV8VRTHB9fNLKN9q66cV27lBPDExYG0UUag8mU3mEg,945
13
+ kiarina/lib/google/auth/_utils/get_user_account_credentials.py,sha256=ihko1Xf_e5EkZ1c5CjAYl8NOvjj6dKtvHl8TH_oLbTU,2353
14
+ kiarina_lib_google_auth-1.4.0.dist-info/METADATA,sha256=_SKLvKqDNMu2Afk2Lg1WaT_It1JGyKora7Bw3y6gp-c,15973
15
+ kiarina_lib_google_auth-1.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
+ kiarina_lib_google_auth-1.4.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any