okta-auth-cli 0.1.1__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.
- okta_auth_cli-0.1.1/.github/CODEOWNERS +1 -0
- okta_auth_cli-0.1.1/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
- okta_auth_cli-0.1.1/.github/ISSUE_TEMPLATE/config.yml +5 -0
- okta_auth_cli-0.1.1/.github/ISSUE_TEMPLATE/feature_request.md +13 -0
- okta_auth_cli-0.1.1/.github/dependabot.yml +12 -0
- okta_auth_cli-0.1.1/.github/pull_request_template.md +14 -0
- okta_auth_cli-0.1.1/.github/workflows/ci.yml +55 -0
- okta_auth_cli-0.1.1/.github/workflows/dependency-review.yml +15 -0
- okta_auth_cli-0.1.1/.github/workflows/release.yml +50 -0
- okta_auth_cli-0.1.1/.gitignore +22 -0
- okta_auth_cli-0.1.1/.pre-commit-config.yaml +7 -0
- okta_auth_cli-0.1.1/AGENTS.md +61 -0
- okta_auth_cli-0.1.1/CLAUDE.md +61 -0
- okta_auth_cli-0.1.1/CONTRIBUTING.md +23 -0
- okta_auth_cli-0.1.1/LICENSE +21 -0
- okta_auth_cli-0.1.1/PKG-INFO +264 -0
- okta_auth_cli-0.1.1/README.md +234 -0
- okta_auth_cli-0.1.1/SECURITY.md +19 -0
- okta_auth_cli-0.1.1/pyproject.toml +76 -0
- okta_auth_cli-0.1.1/src/okta_auth/__init__.py +1 -0
- okta_auth_cli-0.1.1/src/okta_auth/auth/__init__.py +0 -0
- okta_auth_cli-0.1.1/src/okta_auth/auth/login.py +352 -0
- okta_auth_cli-0.1.1/src/okta_auth/auth/session_store.py +132 -0
- okta_auth_cli-0.1.1/src/okta_auth/auth/totp.py +8 -0
- okta_auth_cli-0.1.1/src/okta_auth/browser/__init__.py +0 -0
- okta_auth_cli-0.1.1/src/okta_auth/browser/controller.py +126 -0
- okta_auth_cli-0.1.1/src/okta_auth/browser/detection.py +140 -0
- okta_auth_cli-0.1.1/src/okta_auth/browser/helpers.py +50 -0
- okta_auth_cli-0.1.1/src/okta_auth/cli.py +261 -0
- okta_auth_cli-0.1.1/src/okta_auth/log.py +18 -0
- okta_auth_cli-0.1.1/src/okta_auth/server.py +240 -0
- okta_auth_cli-0.1.1/tests/test_cli.py +86 -0
- okta_auth_cli-0.1.1/tests/test_packaging.py +17 -0
- okta_auth_cli-0.1.1/tests/test_server_smoke.py +14 -0
- okta_auth_cli-0.1.1/tests/test_session_store.py +70 -0
- okta_auth_cli-0.1.1/uv.lock +1085 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @bunizao
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Report an issue in okta-auth-mcp
|
|
4
|
+
labels: bug
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Description
|
|
8
|
+
|
|
9
|
+
## Steps to Reproduce
|
|
10
|
+
|
|
11
|
+
1.
|
|
12
|
+
2.
|
|
13
|
+
3.
|
|
14
|
+
|
|
15
|
+
## Expected Behavior
|
|
16
|
+
|
|
17
|
+
## Actual Behavior
|
|
18
|
+
|
|
19
|
+
## Environment
|
|
20
|
+
|
|
21
|
+
- OS:
|
|
22
|
+
- Python version:
|
|
23
|
+
- Package version:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
-
|
|
4
|
+
|
|
5
|
+
## Validation
|
|
6
|
+
|
|
7
|
+
- [ ] `ruff format --check .`
|
|
8
|
+
- [ ] `ruff check .`
|
|
9
|
+
- [ ] `pytest`
|
|
10
|
+
|
|
11
|
+
## Security
|
|
12
|
+
|
|
13
|
+
- [ ] No secrets/session artifacts were added to the repo
|
|
14
|
+
- [ ] Changes to auth/session paths considered threat model
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main, master]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
lint:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v6
|
|
17
|
+
|
|
18
|
+
- name: Setup Python
|
|
19
|
+
uses: actions/setup-python@v6
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install -U pip
|
|
26
|
+
pip install -e .[dev]
|
|
27
|
+
|
|
28
|
+
- name: Ruff format check
|
|
29
|
+
run: ruff format --check .
|
|
30
|
+
|
|
31
|
+
- name: Ruff lint
|
|
32
|
+
run: ruff check .
|
|
33
|
+
|
|
34
|
+
test:
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
strategy:
|
|
37
|
+
fail-fast: false
|
|
38
|
+
matrix:
|
|
39
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
40
|
+
steps:
|
|
41
|
+
- name: Checkout
|
|
42
|
+
uses: actions/checkout@v6
|
|
43
|
+
|
|
44
|
+
- name: Setup Python
|
|
45
|
+
uses: actions/setup-python@v6
|
|
46
|
+
with:
|
|
47
|
+
python-version: ${{ matrix.python-version }}
|
|
48
|
+
|
|
49
|
+
- name: Install dependencies
|
|
50
|
+
run: |
|
|
51
|
+
python -m pip install -U pip
|
|
52
|
+
pip install -e .[dev]
|
|
53
|
+
|
|
54
|
+
- name: Run tests
|
|
55
|
+
run: pytest
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: Dependency Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
dependency-review:
|
|
11
|
+
if: ${{ !github.event.repository.private }}
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Dependency review
|
|
15
|
+
uses: actions/dependency-review-action@v4
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v6
|
|
17
|
+
|
|
18
|
+
- name: Setup Python
|
|
19
|
+
uses: actions/setup-python@v6
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
|
|
23
|
+
- name: Build artifacts
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install -U pip build twine
|
|
26
|
+
python -m build
|
|
27
|
+
twine check dist/*
|
|
28
|
+
|
|
29
|
+
- name: Upload artifacts
|
|
30
|
+
uses: actions/upload-artifact@v7
|
|
31
|
+
with:
|
|
32
|
+
name: dist
|
|
33
|
+
path: dist/
|
|
34
|
+
|
|
35
|
+
publish:
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
environment: pypi
|
|
39
|
+
permissions:
|
|
40
|
+
id-token: write
|
|
41
|
+
contents: read
|
|
42
|
+
steps:
|
|
43
|
+
- name: Download artifacts
|
|
44
|
+
uses: actions/download-artifact@v8
|
|
45
|
+
with:
|
|
46
|
+
name: dist
|
|
47
|
+
path: dist/
|
|
48
|
+
|
|
49
|
+
- name: Publish to PyPI
|
|
50
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# macOS
|
|
2
|
+
.DS_Store
|
|
3
|
+
|
|
4
|
+
# Python caches
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
|
|
13
|
+
# Tool caches
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
|
|
18
|
+
# Build artifacts
|
|
19
|
+
build/
|
|
20
|
+
dist/
|
|
21
|
+
*.egg-info/
|
|
22
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
okta-auth is an Okta login toolkit with an interactive CLI and an MCP server for session reuse. It uses Playwright to automate Okta SSO and persists per-domain session state for reuse by AI agents.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Setup
|
|
13
|
+
uv venv && source .venv/bin/activate
|
|
14
|
+
uv pip install -e '.[dev]'
|
|
15
|
+
playwright install chromium
|
|
16
|
+
|
|
17
|
+
# Quality gates (run all three before submitting)
|
|
18
|
+
ruff format --check .
|
|
19
|
+
ruff check .
|
|
20
|
+
pytest
|
|
21
|
+
|
|
22
|
+
# Run single test
|
|
23
|
+
pytest tests/test_session_store.py -v
|
|
24
|
+
|
|
25
|
+
# Run interactive CLI
|
|
26
|
+
okta
|
|
27
|
+
|
|
28
|
+
# Run MCP server (stdio transport, stdout is JSON-RPC)
|
|
29
|
+
okta-auth
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Architecture
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
src/okta_auth/
|
|
36
|
+
├── server.py # FastMCP entry point, defines all 5 MCP tools
|
|
37
|
+
├── log.py # Stderr-only logging (stdout reserved for JSON-RPC)
|
|
38
|
+
├── auth/
|
|
39
|
+
│ ├── login.py # auto_login() engine: selector-based form filling, portal detection, MFA/TOTP
|
|
40
|
+
│ ├── session_store.py # Per-domain session CRUD at ~/.okta-auth/sessions/
|
|
41
|
+
│ └── totp.py # pyotp wrapper for TOTP code generation
|
|
42
|
+
└── browser/
|
|
43
|
+
├── controller.py # BrowserController async context manager, BrowserConfig dataclass
|
|
44
|
+
├── detection.py # Cross-platform browser executable discovery with env var overrides
|
|
45
|
+
└── helpers.py # fill_first_match(), click_first_match(), maybe_switch_to_code_factor()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Key Patterns
|
|
49
|
+
|
|
50
|
+
- **Selector redundancy**: `login.py` defines 16+ CSS selectors per form field (USERNAME_SELECTORS, PASSWORD_SELECTORS, etc.) tried in priority order for broad Okta form compatibility.
|
|
51
|
+
- **Portal detection**: `_is_on_portal()` confirms auth completion by checking domain change + login field absence.
|
|
52
|
+
- **Session keying**: Sessions are stored as `{domain_hash}.json` + `{domain_hash}.meta.json` using the URL's netloc as the domain key.
|
|
53
|
+
- **Async-first**: All I/O uses `async/await`; the server runs on asyncio via FastMCP.
|
|
54
|
+
- **Browser fallback**: `BrowserController` tries the requested channel first, falls back to default Chromium on launch failure.
|
|
55
|
+
|
|
56
|
+
## Code Style
|
|
57
|
+
|
|
58
|
+
- **Formatter/Linter**: Ruff (line-length 100, target py311, double quotes, LF endings)
|
|
59
|
+
- **Lint rules**: E, F, I (isort), B (flake8-bugbear); E501 ignored
|
|
60
|
+
- **Type checking**: mypy with `check_untyped_defs = true`, `ignore_missing_imports = true`
|
|
61
|
+
- **All logs go to stderr** — never print to stdout (breaks MCP stdio transport)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
okta-auth is an Okta login toolkit with an interactive CLI and an MCP server for session reuse. It uses Playwright to automate Okta SSO and persists per-domain session state for reuse by AI agents.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Setup
|
|
13
|
+
uv venv && source .venv/bin/activate
|
|
14
|
+
uv pip install -e '.[dev]'
|
|
15
|
+
playwright install chromium
|
|
16
|
+
|
|
17
|
+
# Quality gates (run all three before submitting)
|
|
18
|
+
ruff format --check .
|
|
19
|
+
ruff check .
|
|
20
|
+
pytest
|
|
21
|
+
|
|
22
|
+
# Run single test
|
|
23
|
+
pytest tests/test_session_store.py -v
|
|
24
|
+
|
|
25
|
+
# Run interactive CLI
|
|
26
|
+
okta
|
|
27
|
+
|
|
28
|
+
# Run MCP server (stdio transport, stdout is JSON-RPC)
|
|
29
|
+
okta-auth
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Architecture
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
src/okta_auth/
|
|
36
|
+
├── server.py # FastMCP entry point, defines all 5 MCP tools
|
|
37
|
+
├── log.py # Stderr-only logging (stdout reserved for JSON-RPC)
|
|
38
|
+
├── auth/
|
|
39
|
+
│ ├── login.py # auto_login() engine: selector-based form filling, portal detection, MFA/TOTP
|
|
40
|
+
│ ├── session_store.py # Per-domain session CRUD at ~/.okta-auth/sessions/
|
|
41
|
+
│ └── totp.py # pyotp wrapper for TOTP code generation
|
|
42
|
+
└── browser/
|
|
43
|
+
├── controller.py # BrowserController async context manager, BrowserConfig dataclass
|
|
44
|
+
├── detection.py # Cross-platform browser executable discovery with env var overrides
|
|
45
|
+
└── helpers.py # fill_first_match(), click_first_match(), maybe_switch_to_code_factor()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Key Patterns
|
|
49
|
+
|
|
50
|
+
- **Selector redundancy**: `login.py` defines 16+ CSS selectors per form field (USERNAME_SELECTORS, PASSWORD_SELECTORS, etc.) tried in priority order for broad Okta form compatibility.
|
|
51
|
+
- **Portal detection**: `_is_on_portal()` confirms auth completion by checking domain change + login field absence.
|
|
52
|
+
- **Session keying**: Sessions are stored as `{domain_hash}.json` + `{domain_hash}.meta.json` using the URL's netloc as the domain key.
|
|
53
|
+
- **Async-first**: All I/O uses `async/await`; the server runs on asyncio via FastMCP.
|
|
54
|
+
- **Browser fallback**: `BrowserController` tries the requested channel first, falls back to default Chromium on launch failure.
|
|
55
|
+
|
|
56
|
+
## Code Style
|
|
57
|
+
|
|
58
|
+
- **Formatter/Linter**: Ruff (line-length 100, target py311, double quotes, LF endings)
|
|
59
|
+
- **Lint rules**: E, F, I (isort), B (flake8-bugbear); E501 ignored
|
|
60
|
+
- **Type checking**: mypy with `check_untyped_defs = true`, `ignore_missing_imports = true`
|
|
61
|
+
- **All logs go to stderr** — never print to stdout (breaks MCP stdio transport)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
uv venv && source .venv/bin/activate
|
|
7
|
+
uv pip install -e '.[dev]'
|
|
8
|
+
playwright install chromium
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Local Quality Gates
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
ruff format --check .
|
|
15
|
+
ruff check .
|
|
16
|
+
pytest
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Pull Requests
|
|
20
|
+
|
|
21
|
+
- Keep PRs focused and small.
|
|
22
|
+
- Add/update tests for behavior changes.
|
|
23
|
+
- Do not commit secrets, cookies, or local session files.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 bunizao
|
|
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,264 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: okta-auth-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Okta login toolkit with an interactive CLI and MCP session reuse
|
|
5
|
+
Project-URL: Homepage, https://github.com/bunizao/okta-auth
|
|
6
|
+
Project-URL: Issues, https://github.com/bunizao/okta-auth/issues
|
|
7
|
+
Author: bunizao
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: authentication,mcp,okta,playwright,sso
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: mcp[cli]>=1.9.0
|
|
21
|
+
Requires-Dist: playwright>=1.54.0
|
|
22
|
+
Requires-Dist: pydantic>=2.0.0
|
|
23
|
+
Requires-Dist: pyotp>=2.9.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: mypy>=1.11.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.2.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.6.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# okta-auth
|
|
32
|
+
|
|
33
|
+
> **Alpha** — this project is under active development and iterating quickly.
|
|
34
|
+
> APIs, tool signatures, and session formats may change between releases.
|
|
35
|
+
|
|
36
|
+
Okta login toolkit with two entry points:
|
|
37
|
+
|
|
38
|
+
- `okta`: interactive CLI for humans to log in and save a session locally
|
|
39
|
+
- `okta-auth`: MCP server for AI agents that reuse those sessions
|
|
40
|
+
|
|
41
|
+
## CLI Usage
|
|
42
|
+
|
|
43
|
+
Run `okta` with no arguments to start an interactive login flow:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
okta
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
You can also pass values directly:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
okta https://portal.company.com --username you@company.com
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The login flow is headless by default. Pass `--headed` if you want to see the browser window.
|
|
56
|
+
|
|
57
|
+
Available commands:
|
|
58
|
+
|
|
59
|
+
- `okta [url]`: log in and save a session
|
|
60
|
+
- `okta check <url>`: verify a saved session
|
|
61
|
+
- `okta list`: list saved sessions
|
|
62
|
+
- `okta delete <url>`: delete a saved session
|
|
63
|
+
- `okta cookies <url>`: inspect stored cookies
|
|
64
|
+
|
|
65
|
+
## MCP Tools
|
|
66
|
+
|
|
67
|
+
| Tool | Description |
|
|
68
|
+
|------|-------------|
|
|
69
|
+
| `okta_login` | Authenticate to a target URL and store session state |
|
|
70
|
+
| `okta_check_session` | Verify whether a stored session is still valid |
|
|
71
|
+
| `okta_list_sessions` | List saved sessions and metadata |
|
|
72
|
+
| `okta_delete_session` | Remove a stored session |
|
|
73
|
+
| `okta_get_cookies` | Retrieve cookies from stored session (sensitive) |
|
|
74
|
+
|
|
75
|
+
Sessions are stored under `~/.okta-auth/sessions/`. Existing sessions under
|
|
76
|
+
`~/.okta-auth-mcp/sessions/` are migrated automatically.
|
|
77
|
+
|
|
78
|
+
## Security Model
|
|
79
|
+
|
|
80
|
+
- This project is intended for **local trusted execution**.
|
|
81
|
+
- Session files and cookies are sensitive credentials; protect the host account.
|
|
82
|
+
- Prefer private/internal usage unless security controls are reviewed.
|
|
83
|
+
- **Never pass credentials as tool arguments** — use environment variables so that AI agents never see your username, password, or TOTP secret in their context.
|
|
84
|
+
|
|
85
|
+
## Credentials Setup
|
|
86
|
+
|
|
87
|
+
### Environment Variables
|
|
88
|
+
|
|
89
|
+
Set credentials in your shell profile so they are inherited by the CLI or MCP server process.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Add to ~/.zshrc or ~/.zprofile (zsh) / ~/.bashrc (bash)
|
|
93
|
+
export OKTA_USERNAME="you@company.com"
|
|
94
|
+
export OKTA_PASSWORD="your-okta-password"
|
|
95
|
+
export OKTA_TOTP_SECRET="JBSWY3DPEHPK3PXP" # only if MFA is enabled
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
After editing, reload your shell or open a new terminal, then restart the AI Agent.
|
|
99
|
+
|
|
100
|
+
The AI agent can then log in with just the URL:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
okta_login(url="https://portal.company.com")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Explicit arguments still override environment variables if needed.
|
|
107
|
+
|
|
108
|
+
### 1Password CLI
|
|
109
|
+
|
|
110
|
+
[`op run`](https://developer.1password.com/docs/cli/secrets-scripts/) injects secrets at process launch time. No plaintext credentials appear in shell profiles, config files, or environment variables — they live only in 1Password.
|
|
111
|
+
|
|
112
|
+
**1. Store credentials in 1Password** (one-time setup):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
op item create --category login --title "Okta MCP" \
|
|
116
|
+
username="you@company.com" \
|
|
117
|
+
password="your-okta-password" \
|
|
118
|
+
totp_secret="JBSWY3DPEHPK3PXP"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**2. Create a secrets reference file** at `~/.okta-auth/.env` (contains paths, not values):
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
OKTA_USERNAME=op://Personal/Okta MCP/username
|
|
125
|
+
OKTA_PASSWORD=op://Personal/Okta MCP/password
|
|
126
|
+
OKTA_TOTP_SECRET=op://Personal/Okta MCP/totp_secret
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**3. Update your MCP client config** to wrap the server with `op run`:
|
|
130
|
+
|
|
131
|
+
_Claude Code:_
|
|
132
|
+
```bash
|
|
133
|
+
claude mcp add okta-auth -- op run --env-file=$HOME/.okta-auth/.env -- uvx --from okta-auth-cli okta-auth
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
_Claude Desktop / Cursor / Windsurf:_
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"mcpServers": {
|
|
140
|
+
"okta-auth": {
|
|
141
|
+
"command": "op",
|
|
142
|
+
"args": ["run", "--env-file=/Users/yourname/.okta-auth/.env", "--", "uvx", "--from", "okta-auth-cli", "okta-auth"]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`op run` prompts for biometric/Touch ID once per session. Install 1Password CLI via `brew install 1password-cli`.
|
|
149
|
+
|
|
150
|
+
### macOS Keychain
|
|
151
|
+
|
|
152
|
+
A built-in alternative that requires no extra tools. Credentials are stored in the system Keychain and fetched on each shell startup.
|
|
153
|
+
|
|
154
|
+
**Store** (one-time, run in terminal):
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
security add-generic-password -a okta-mcp -s OKTA_USERNAME -w "you@company.com"
|
|
158
|
+
security add-generic-password -a okta-mcp -s OKTA_PASSWORD -w "your-okta-password"
|
|
159
|
+
security add-generic-password -a okta-mcp -s OKTA_TOTP_SECRET -w "JBSWY3DPEHPK3PXP"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Load** in `~/.zshrc` or `~/.zprofile`:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
export OKTA_USERNAME=$(security find-generic-password -a okta-mcp -s OKTA_USERNAME -w 2>/dev/null)
|
|
166
|
+
export OKTA_PASSWORD=$(security find-generic-password -a okta-mcp -s OKTA_PASSWORD -w 2>/dev/null)
|
|
167
|
+
export OKTA_TOTP_SECRET=$(security find-generic-password -a okta-mcp -s OKTA_TOTP_SECRET -w 2>/dev/null)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
macOS may prompt for Keychain access on the first load after a reboot.
|
|
171
|
+
|
|
172
|
+
### How to Get Your TOTP Secret Key
|
|
173
|
+
|
|
174
|
+
The TOTP secret is the Base32 key (16–32 uppercase letters and digits) that backs your authenticator app. You need to obtain it **during the initial MFA enrollment** — it cannot be retrieved from an already-configured authenticator app.
|
|
175
|
+
|
|
176
|
+
#### During Okta MFA setup
|
|
177
|
+
|
|
178
|
+
1. In Okta, go to **Settings → Security Methods** (or follow your admin's enrollment link).
|
|
179
|
+
2. Choose **Google Authenticator** as the factor type.
|
|
180
|
+
3. The QR code screen also shows a **"Can't scan?"** link — click it.
|
|
181
|
+
4. Copy the displayed text key (e.g. `JBSWY3DPEHPK3PXP`). This is your `OKTA_TOTP_SECRET`.
|
|
182
|
+
5. Finish enrollment by entering the 6-digit code from your authenticator app to confirm.
|
|
183
|
+
|
|
184
|
+
> This project does **not** currently support portals that use **only** the Okta Verify app for MFA.
|
|
185
|
+
|
|
186
|
+
#### Already enrolled and lost the secret?
|
|
187
|
+
|
|
188
|
+
You must **re-enroll** the authenticator factor to obtain a new secret:
|
|
189
|
+
|
|
190
|
+
1. Go to **Okta → Settings → Security Methods**.
|
|
191
|
+
2. Remove the existing authenticator entry.
|
|
192
|
+
3. Re-add it and follow the steps above to capture the secret before scanning the QR code.
|
|
193
|
+
|
|
194
|
+
## Installation
|
|
195
|
+
|
|
196
|
+
### With uv tool
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
uv tool install okta-auth-cli
|
|
200
|
+
okta
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### With pipx
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
pipx install okta-auth-cli
|
|
207
|
+
okta
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### With pip
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
pip install okta-auth-cli
|
|
214
|
+
okta
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Browser setup
|
|
218
|
+
|
|
219
|
+
The server uses Playwright for browser automation. It **automatically detects and prefers your system Chrome/Edge** — no extra download required if you already have one installed.
|
|
220
|
+
|
|
221
|
+
If no system browser is found, install the Playwright-bundled Chromium as fallback:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
playwright install chromium
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## MCP Client Configuration
|
|
228
|
+
|
|
229
|
+
### Claude Code
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
claude mcp add okta-auth -- uvx --from okta-auth-cli okta-auth
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Claude Desktop / Cursor / Windsurf
|
|
236
|
+
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"mcpServers": {
|
|
240
|
+
"okta-auth": {
|
|
241
|
+
"command": "uvx",
|
|
242
|
+
"args": ["--from", "okta-auth-cli", "okta-auth"]
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Use `okta` for the interactive CLI. Use `okta-auth` only when wiring the package into an MCP client.
|
|
249
|
+
|
|
250
|
+
## Development
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
uv venv && source .venv/bin/activate
|
|
254
|
+
uv pip install -e '.[dev]'
|
|
255
|
+
playwright install chromium
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Run checks locally:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
ruff format --check .
|
|
262
|
+
ruff check .
|
|
263
|
+
pytest
|
|
264
|
+
```
|