go-authgate 0.1.0__tar.gz

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.
Files changed (53) hide show
  1. go_authgate-0.1.0/.github/workflows/release.yml +33 -0
  2. go_authgate-0.1.0/.github/workflows/testing.yml +36 -0
  3. go_authgate-0.1.0/.github/workflows/trivy.yml +35 -0
  4. go_authgate-0.1.0/.gitignore +15 -0
  5. go_authgate-0.1.0/CLAUDE.md +44 -0
  6. go_authgate-0.1.0/LICENSE +21 -0
  7. go_authgate-0.1.0/Makefile +24 -0
  8. go_authgate-0.1.0/PKG-INFO +142 -0
  9. go_authgate-0.1.0/README.md +108 -0
  10. go_authgate-0.1.0/pyproject.toml +90 -0
  11. go_authgate-0.1.0/src/authgate/__init__.py +137 -0
  12. go_authgate-0.1.0/src/authgate/_version.py +3 -0
  13. go_authgate-0.1.0/src/authgate/authflow/__init__.py +18 -0
  14. go_authgate-0.1.0/src/authgate/authflow/authcode.py +138 -0
  15. go_authgate-0.1.0/src/authgate/authflow/browser.py +43 -0
  16. go_authgate-0.1.0/src/authgate/authflow/device.py +120 -0
  17. go_authgate-0.1.0/src/authgate/authflow/pkce.py +28 -0
  18. go_authgate-0.1.0/src/authgate/authflow/token_source.py +126 -0
  19. go_authgate-0.1.0/src/authgate/clientcreds/__init__.py +11 -0
  20. go_authgate-0.1.0/src/authgate/clientcreds/token_source.py +121 -0
  21. go_authgate-0.1.0/src/authgate/clientcreds/transport.py +37 -0
  22. go_authgate-0.1.0/src/authgate/credstore/__init__.py +54 -0
  23. go_authgate-0.1.0/src/authgate/credstore/codecs.py +45 -0
  24. go_authgate-0.1.0/src/authgate/credstore/file_store.py +154 -0
  25. go_authgate-0.1.0/src/authgate/credstore/keyring_store.py +68 -0
  26. go_authgate-0.1.0/src/authgate/credstore/models.py +28 -0
  27. go_authgate-0.1.0/src/authgate/credstore/protocols.py +43 -0
  28. go_authgate-0.1.0/src/authgate/credstore/secure_store.py +100 -0
  29. go_authgate-0.1.0/src/authgate/discovery/__init__.py +6 -0
  30. go_authgate-0.1.0/src/authgate/discovery/async_client.py +72 -0
  31. go_authgate-0.1.0/src/authgate/discovery/client.py +109 -0
  32. go_authgate-0.1.0/src/authgate/discovery/models.py +43 -0
  33. go_authgate-0.1.0/src/authgate/exceptions.py +45 -0
  34. go_authgate-0.1.0/src/authgate/middleware/__init__.py +11 -0
  35. go_authgate-0.1.0/src/authgate/middleware/core.py +67 -0
  36. go_authgate-0.1.0/src/authgate/middleware/django.py +105 -0
  37. go_authgate-0.1.0/src/authgate/middleware/fastapi.py +95 -0
  38. go_authgate-0.1.0/src/authgate/middleware/flask.py +103 -0
  39. go_authgate-0.1.0/src/authgate/middleware/models.py +20 -0
  40. go_authgate-0.1.0/src/authgate/oauth/__init__.py +21 -0
  41. go_authgate-0.1.0/src/authgate/oauth/async_client.py +237 -0
  42. go_authgate-0.1.0/src/authgate/oauth/client.py +237 -0
  43. go_authgate-0.1.0/src/authgate/oauth/models.py +96 -0
  44. go_authgate-0.1.0/src/authgate/py.typed +0 -0
  45. go_authgate-0.1.0/tests/__init__.py +0 -0
  46. go_authgate-0.1.0/tests/conftest.py +21 -0
  47. go_authgate-0.1.0/tests/test_authflow.py +209 -0
  48. go_authgate-0.1.0/tests/test_clientcreds.py +146 -0
  49. go_authgate-0.1.0/tests/test_credstore.py +184 -0
  50. go_authgate-0.1.0/tests/test_discovery.py +139 -0
  51. go_authgate-0.1.0/tests/test_middleware.py +149 -0
  52. go_authgate-0.1.0/tests/test_oauth.py +301 -0
  53. go_authgate-0.1.0/uv.lock +1228 -0
@@ -0,0 +1,33 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ test:
9
+ uses: ./.github/workflows/testing.yml
10
+
11
+ trivy:
12
+ uses: ./.github/workflows/trivy.yml
13
+
14
+ publish:
15
+ needs: [test, trivy]
16
+ runs-on: ubuntu-latest
17
+ permissions:
18
+ id-token: write
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v6
25
+
26
+ - name: Set up Python
27
+ run: uv python install 3.12
28
+
29
+ - name: Build
30
+ run: uv build
31
+
32
+ - name: Publish to PyPI
33
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,36 @@
1
+ name: Testing
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ workflow_call:
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v6
22
+
23
+ - name: Set up Python ${{ matrix.python-version }}
24
+ run: uv python install ${{ matrix.python-version }}
25
+
26
+ - name: Install dependencies
27
+ run: make install
28
+
29
+ - name: Lint
30
+ run: make lint
31
+
32
+ - name: Type check
33
+ run: make typecheck
34
+
35
+ - name: Test
36
+ run: make test
@@ -0,0 +1,35 @@
1
+ name: Trivy Security Scan
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ schedule:
9
+ - cron: "0 15 * * *" # 23:00 UTC+8
10
+ workflow_call:
11
+
12
+ jobs:
13
+ trivy:
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ security-events: write
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Trivy vulnerability scan
22
+ uses: aquasecurity/trivy-action@0.35.0
23
+ with:
24
+ scan-type: fs
25
+ scan-ref: .
26
+ format: sarif
27
+ output: trivy-results.sarif
28
+ exit-code: 1
29
+ severity: CRITICAL,HIGH
30
+
31
+ - name: Upload results to GitHub Security tab
32
+ uses: github/codeql-action/upload-sarif@v3
33
+ if: always()
34
+ with:
35
+ sarif_file: trivy-results.sarif
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ *.egg
8
+ .mypy_cache/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .coverage
12
+ htmlcov/
13
+ *.so
14
+ .venv/
15
+ venv/
@@ -0,0 +1,44 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code when working with the Python SDK.
4
+
5
+ ## Project Overview
6
+
7
+ Python SDK for AuthGate — mirrors the Go SDK's architecture using idiomatic Python patterns.
8
+
9
+ Package: `authgate` (Python 3.10+, src layout with hatchling build)
10
+
11
+ ## Common Commands
12
+
13
+ ```bash
14
+ make install # uv sync --all-extras (install all deps)
15
+ make test # Run all tests with pytest
16
+ make lint # Run ruff linter
17
+ make fmt # Format code with ruff
18
+ make typecheck # Run mypy strict
19
+ ```
20
+
21
+ Project is managed with [uv](https://docs.astral.sh/uv/). All `make` targets use `uv run` so no manual venv activation is needed.
22
+
23
+ ## Code Style
24
+
25
+ - ruff for linting and formatting (line length 100)
26
+ - mypy strict mode
27
+ - Dataclasses over Pydantic (zero extra deps)
28
+ - Sync + Async dual API in separate client classes
29
+ - Framework-specific middleware: users only import what they need
30
+ - `from __future__ import annotations` in all modules
31
+
32
+ ## Architecture
33
+
34
+ - `oauth/` — Pure HTTP client layer (sync: `OAuthClient`, async: `AsyncOAuthClient`)
35
+ - `discovery/` — OIDC auto-discovery with caching
36
+ - `credstore/` — Generic credential storage (file, keyring, composite secure store)
37
+ - `authflow/` — Device Code flow, Auth Code + PKCE, auto-refresh TokenSource
38
+ - `middleware/` — Framework adapters (FastAPI, Flask, Django)
39
+ - `clientcreds/` — M2M Client Credentials with auto-caching
40
+ - `__init__.py` — `authenticate()` / `async_authenticate()` entry points
41
+
42
+ ## Before Committing
43
+
44
+ All code must pass `make lint`, `make fmt`, and `make typecheck` before committing.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 AuthGate Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,24 @@
1
+ .PHONY: test lint fmt typecheck install clean
2
+
3
+ install:
4
+ uv sync --all-extras
5
+
6
+ test:
7
+ uv run pytest tests/ -v --tb=short
8
+
9
+ coverage:
10
+ uv run coverage run -m pytest tests/ -v --tb=short
11
+ uv run coverage report -m
12
+
13
+ lint:
14
+ uv run ruff check src/ tests/
15
+
16
+ fmt:
17
+ uv run ruff format src/ tests/
18
+ uv run ruff check --fix src/ tests/
19
+
20
+ typecheck:
21
+ uv run mypy src/authgate/
22
+
23
+ clean:
24
+ rm -rf build/ dist/ *.egg-info src/*.egg-info .mypy_cache .pytest_cache .ruff_cache .coverage htmlcov/
@@ -0,0 +1,142 @@
1
+ Metadata-Version: 2.4
2
+ Name: go-authgate
3
+ Version: 0.1.0
4
+ Summary: Python SDK for AuthGate — OAuth 2.0 authentication and token management
5
+ Author: AuthGate Contributors
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: httpx<1,>=0.27
19
+ Requires-Dist: keyring<27,>=25
20
+ Provides-Extra: dev
21
+ Requires-Dist: coverage>=7; extra == 'dev'
22
+ Requires-Dist: mypy>=1.10; extra == 'dev'
23
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
24
+ Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
25
+ Requires-Dist: pytest>=8; extra == 'dev'
26
+ Requires-Dist: ruff>=0.5; extra == 'dev'
27
+ Provides-Extra: django
28
+ Requires-Dist: django>=4.2; extra == 'django'
29
+ Provides-Extra: fastapi
30
+ Requires-Dist: fastapi>=0.100; extra == 'fastapi'
31
+ Provides-Extra: flask
32
+ Requires-Dist: flask>=2.3; extra == 'flask'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # AuthGate Python SDK
36
+
37
+ [![PyPI](https://img.shields.io/pypi/v/go-authgate)](https://pypi.org/project/go-authgate/)
38
+ [![Python](https://img.shields.io/pypi/pyversions/go-authgate)](https://pypi.org/project/go-authgate/)
39
+ [![CI](https://github.com/go-authgate/sdk-python/actions/workflows/testing.yml/badge.svg)](https://github.com/go-authgate/sdk-python/actions/workflows/testing.yml)
40
+ [![License](https://img.shields.io/pypi/l/go-authgate)](LICENSE)
41
+
42
+ Python SDK for [AuthGate](https://github.com/go-authgate) — OAuth 2.0 authentication and token management.
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ pip install go-authgate
48
+ ```
49
+
50
+ With framework support:
51
+
52
+ ```bash
53
+ pip install go-authgate[fastapi]
54
+ pip install go-authgate[flask]
55
+ pip install go-authgate[django]
56
+ ```
57
+
58
+ ## Quick Start
59
+
60
+ ```python
61
+ from authgate import authenticate
62
+
63
+ client, token = authenticate(
64
+ "https://auth.example.com",
65
+ "my-client-id",
66
+ scopes=["profile", "email"],
67
+ )
68
+
69
+ print(f"Access token: {token.access_token}")
70
+ ```
71
+
72
+ ## Async Usage
73
+
74
+ ```python
75
+ from authgate import async_authenticate
76
+
77
+ client, token = await async_authenticate(
78
+ "https://auth.example.com",
79
+ "my-client-id",
80
+ scopes=["profile", "email"],
81
+ )
82
+ ```
83
+
84
+ ## Client Credentials (M2M)
85
+
86
+ ```python
87
+ from authgate.oauth import OAuthClient, Endpoints
88
+ from authgate.clientcreds import TokenSource, BearerAuth
89
+ import httpx
90
+
91
+ client = OAuthClient("my-service", endpoints, client_secret="secret")
92
+ ts = TokenSource(client, scopes=["api"])
93
+
94
+ # Auto-attaches Bearer token to every request
95
+ with httpx.Client(auth=BearerAuth(ts)) as http:
96
+ resp = http.get("https://api.example.com/data")
97
+ ```
98
+
99
+ ## Middleware
100
+
101
+ ### FastAPI
102
+
103
+ ```python
104
+ from fastapi import FastAPI, Depends
105
+ from authgate.middleware.fastapi import BearerAuth
106
+ from authgate.middleware.models import TokenInfo
107
+
108
+ app = FastAPI()
109
+ auth = BearerAuth(oauth_client)
110
+
111
+ @app.get("/protected")
112
+ async def protected(info: TokenInfo = Depends(auth)):
113
+ return {"user": info.user_id}
114
+ ```
115
+
116
+ ### Flask
117
+
118
+ ```python
119
+ from flask import Flask
120
+ from authgate.middleware.flask import bearer_auth, get_token_info
121
+
122
+ app = Flask(__name__)
123
+
124
+ @app.route("/protected")
125
+ @bearer_auth(oauth_client)
126
+ def protected():
127
+ info = get_token_info()
128
+ return {"user": info.user_id}
129
+ ```
130
+
131
+ ## Development
132
+
133
+ ```bash
134
+ make install # uv sync --all-extras
135
+ make test
136
+ make lint
137
+ make typecheck
138
+ ```
139
+
140
+ ## License
141
+
142
+ MIT
@@ -0,0 +1,108 @@
1
+ # AuthGate Python SDK
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/go-authgate)](https://pypi.org/project/go-authgate/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/go-authgate)](https://pypi.org/project/go-authgate/)
5
+ [![CI](https://github.com/go-authgate/sdk-python/actions/workflows/testing.yml/badge.svg)](https://github.com/go-authgate/sdk-python/actions/workflows/testing.yml)
6
+ [![License](https://img.shields.io/pypi/l/go-authgate)](LICENSE)
7
+
8
+ Python SDK for [AuthGate](https://github.com/go-authgate) — OAuth 2.0 authentication and token management.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ pip install go-authgate
14
+ ```
15
+
16
+ With framework support:
17
+
18
+ ```bash
19
+ pip install go-authgate[fastapi]
20
+ pip install go-authgate[flask]
21
+ pip install go-authgate[django]
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ```python
27
+ from authgate import authenticate
28
+
29
+ client, token = authenticate(
30
+ "https://auth.example.com",
31
+ "my-client-id",
32
+ scopes=["profile", "email"],
33
+ )
34
+
35
+ print(f"Access token: {token.access_token}")
36
+ ```
37
+
38
+ ## Async Usage
39
+
40
+ ```python
41
+ from authgate import async_authenticate
42
+
43
+ client, token = await async_authenticate(
44
+ "https://auth.example.com",
45
+ "my-client-id",
46
+ scopes=["profile", "email"],
47
+ )
48
+ ```
49
+
50
+ ## Client Credentials (M2M)
51
+
52
+ ```python
53
+ from authgate.oauth import OAuthClient, Endpoints
54
+ from authgate.clientcreds import TokenSource, BearerAuth
55
+ import httpx
56
+
57
+ client = OAuthClient("my-service", endpoints, client_secret="secret")
58
+ ts = TokenSource(client, scopes=["api"])
59
+
60
+ # Auto-attaches Bearer token to every request
61
+ with httpx.Client(auth=BearerAuth(ts)) as http:
62
+ resp = http.get("https://api.example.com/data")
63
+ ```
64
+
65
+ ## Middleware
66
+
67
+ ### FastAPI
68
+
69
+ ```python
70
+ from fastapi import FastAPI, Depends
71
+ from authgate.middleware.fastapi import BearerAuth
72
+ from authgate.middleware.models import TokenInfo
73
+
74
+ app = FastAPI()
75
+ auth = BearerAuth(oauth_client)
76
+
77
+ @app.get("/protected")
78
+ async def protected(info: TokenInfo = Depends(auth)):
79
+ return {"user": info.user_id}
80
+ ```
81
+
82
+ ### Flask
83
+
84
+ ```python
85
+ from flask import Flask
86
+ from authgate.middleware.flask import bearer_auth, get_token_info
87
+
88
+ app = Flask(__name__)
89
+
90
+ @app.route("/protected")
91
+ @bearer_auth(oauth_client)
92
+ def protected():
93
+ info = get_token_info()
94
+ return {"user": info.user_id}
95
+ ```
96
+
97
+ ## Development
98
+
99
+ ```bash
100
+ make install # uv sync --all-extras
101
+ make test
102
+ make lint
103
+ make typecheck
104
+ ```
105
+
106
+ ## License
107
+
108
+ MIT
@@ -0,0 +1,90 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "go-authgate"
7
+ dynamic = ["version"]
8
+ description = "Python SDK for AuthGate — OAuth 2.0 authentication and token management"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "AuthGate Contributors" }]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = ["httpx>=0.27,<1", "keyring>=25,<27"]
25
+
26
+ [project.optional-dependencies]
27
+ fastapi = ["fastapi>=0.100"]
28
+ flask = ["flask>=2.3"]
29
+ django = ["django>=4.2"]
30
+ dev = [
31
+ "pytest>=8",
32
+ "pytest-asyncio>=0.23",
33
+ "pytest-httpx>=0.30",
34
+ "coverage>=7",
35
+ "mypy>=1.10",
36
+ "ruff>=0.5",
37
+ ]
38
+
39
+ [tool.hatch.version]
40
+ path = "src/authgate/_version.py"
41
+
42
+ [tool.hatch.build.targets.wheel]
43
+ packages = ["src/authgate"]
44
+
45
+ [tool.ruff]
46
+ target-version = "py310"
47
+ line-length = 100
48
+
49
+ [tool.ruff.lint]
50
+ select = [
51
+ "E",
52
+ "F",
53
+ "W",
54
+ "I",
55
+ "N",
56
+ "UP",
57
+ "B",
58
+ "A",
59
+ "SIM",
60
+ "RUF",
61
+ ]
62
+
63
+ [tool.ruff.lint.isort]
64
+ known-first-party = ["authgate"]
65
+
66
+ [tool.mypy]
67
+ python_version = "3.10"
68
+ strict = true
69
+ warn_return_any = true
70
+ warn_unused_configs = true
71
+
72
+ [[tool.mypy.overrides]]
73
+ module = ["keyring", "keyring.errors"]
74
+ ignore_missing_imports = true
75
+
76
+ [[tool.mypy.overrides]]
77
+ module = ["fastapi", "fastapi.*"]
78
+ ignore_missing_imports = true
79
+
80
+ [[tool.mypy.overrides]]
81
+ module = ["flask", "flask.*"]
82
+ ignore_missing_imports = true
83
+
84
+ [[tool.mypy.overrides]]
85
+ module = ["django", "django.*"]
86
+ ignore_missing_imports = true
87
+
88
+ [tool.pytest.ini_options]
89
+ testpaths = ["tests"]
90
+ asyncio_mode = "auto"
@@ -0,0 +1,137 @@
1
+ """AuthGate Python SDK — one-call authentication entry point."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import enum
6
+
7
+ from authgate._version import __version__
8
+ from authgate.authflow.authcode import run_auth_code_flow
9
+ from authgate.authflow.browser import check_browser_availability
10
+ from authgate.authflow.device import async_run_device_flow, run_device_flow
11
+ from authgate.authflow.token_source import TokenSource
12
+ from authgate.credstore import default_token_secure_store
13
+ from authgate.discovery.async_client import AsyncDiscoveryClient
14
+ from authgate.discovery.client import DiscoveryClient
15
+ from authgate.exceptions import AuthGateError
16
+ from authgate.oauth.async_client import AsyncOAuthClient
17
+ from authgate.oauth.client import OAuthClient
18
+ from authgate.oauth.models import Token
19
+
20
+
21
+ class FlowMode(enum.Enum):
22
+ """Authentication flow selection strategy."""
23
+
24
+ AUTO = "auto"
25
+ BROWSER = "browser"
26
+ DEVICE = "device"
27
+
28
+
29
+ def authenticate(
30
+ authgate_url: str,
31
+ client_id: str,
32
+ *,
33
+ scopes: list[str] | None = None,
34
+ service_name: str = "authgate",
35
+ store_path: str = ".authgate-tokens.json",
36
+ local_port: int = 8088,
37
+ flow_mode: FlowMode = FlowMode.AUTO,
38
+ ) -> tuple[OAuthClient, Token]:
39
+ """Authenticate with an AuthGate server and return a ready-to-use client and token.
40
+
41
+ Cached tokens are reused automatically; expired tokens are refreshed.
42
+ When no valid token exists, the flow is determined by ``flow_mode``.
43
+ """
44
+ if not authgate_url:
45
+ raise AuthGateError("authgate: authgate_url is required")
46
+ if not client_id:
47
+ raise AuthGateError("authgate: client_id is required")
48
+
49
+ _scopes = scopes or []
50
+
51
+ # 1. Discover endpoints
52
+ disco = DiscoveryClient(authgate_url)
53
+ meta = disco.fetch()
54
+
55
+ # 2. Create OAuth client
56
+ client = OAuthClient(client_id, meta.to_endpoints())
57
+
58
+ # 3. Set up token store and source
59
+ store = default_token_secure_store(service_name, store_path)
60
+ ts = TokenSource(client, store=store)
61
+
62
+ # 4. Return cached/refreshed token if available
63
+ try:
64
+ token = ts.token()
65
+ return client, token
66
+ except Exception:
67
+ pass
68
+
69
+ # 5. No valid token — run the appropriate authentication flow
70
+ if flow_mode == FlowMode.BROWSER:
71
+ token = run_auth_code_flow(client, _scopes, local_port=local_port)
72
+ elif flow_mode == FlowMode.DEVICE:
73
+ token = run_device_flow(client, _scopes)
74
+ else: # AUTO
75
+ if check_browser_availability():
76
+ token = run_auth_code_flow(client, _scopes, local_port=local_port)
77
+ else:
78
+ token = run_device_flow(client, _scopes)
79
+
80
+ # 6. Persist the new token
81
+ ts.save_token(token)
82
+
83
+ return client, token
84
+
85
+
86
+ async def async_authenticate(
87
+ authgate_url: str,
88
+ client_id: str,
89
+ *,
90
+ scopes: list[str] | None = None,
91
+ service_name: str = "authgate",
92
+ store_path: str = ".authgate-tokens.json",
93
+ flow_mode: FlowMode = FlowMode.AUTO,
94
+ ) -> tuple[AsyncOAuthClient, Token]:
95
+ """Async version of authenticate().
96
+
97
+ Note: Auth Code flow is not available in async mode. Device flow is used for
98
+ BROWSER and AUTO modes when a browser is available.
99
+ """
100
+ if not authgate_url:
101
+ raise AuthGateError("authgate: authgate_url is required")
102
+ if not client_id:
103
+ raise AuthGateError("authgate: client_id is required")
104
+
105
+ _scopes = scopes or []
106
+
107
+ # 1. Discover endpoints
108
+ disco = AsyncDiscoveryClient(authgate_url)
109
+ meta = await disco.fetch()
110
+
111
+ # 2. Create async OAuth client
112
+ client = AsyncOAuthClient(client_id, meta.to_endpoints())
113
+
114
+ # 3. Check stored token (sync store, run in thread)
115
+ store = default_token_secure_store(service_name, store_path)
116
+ ts = TokenSource(OAuthClient(client_id, meta.to_endpoints()), store=store)
117
+ try:
118
+ token = ts.token()
119
+ return client, token
120
+ except Exception:
121
+ pass
122
+
123
+ # 4. Run device flow (always, since auth code needs sync HTTP server)
124
+ token = await async_run_device_flow(client, _scopes)
125
+
126
+ # 5. Persist
127
+ ts.save_token(token)
128
+
129
+ return client, token
130
+
131
+
132
+ __all__ = [
133
+ "FlowMode",
134
+ "__version__",
135
+ "async_authenticate",
136
+ "authenticate",
137
+ ]
@@ -0,0 +1,3 @@
1
+ """Version information."""
2
+
3
+ __version__ = "0.1.0"