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.
- kiarina/lib/google/auth/__init__.py +58 -0
- kiarina/lib/google/auth/_helpers/__init__.py +0 -0
- kiarina/lib/google/auth/_helpers/get_credentials.py +63 -0
- kiarina/lib/google/auth/_helpers/get_self_signed_jwt.py +23 -0
- kiarina/lib/google/auth/_types/__init__.py +0 -0
- kiarina/lib/google/auth/_types/credentials.py +13 -0
- kiarina/lib/google/auth/_types/credentials_cache.py +21 -0
- kiarina/lib/google/auth/_utils/__init__.py +0 -0
- kiarina/lib/google/auth/_utils/get_default_credentials.py +35 -0
- kiarina/lib/google/auth/_utils/get_service_account_credentials.py +27 -0
- kiarina/lib/google/auth/_utils/get_user_account_credentials.py +81 -0
- kiarina/lib/google/auth/py.typed +0 -0
- kiarina/lib/google/auth/settings.py +116 -0
- kiarina_lib_google_auth-1.4.0.dist-info/METADATA +502 -0
- kiarina_lib_google_auth-1.4.0.dist-info/RECORD +16 -0
- kiarina_lib_google_auth-1.4.0.dist-info/WHEEL +4 -0
@@ -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,,
|