nanoidp 1.0.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.
- nanoidp-1.0.0/.gitignore +41 -0
- nanoidp-1.0.0/LICENSE +21 -0
- nanoidp-1.0.0/PKG-INFO +568 -0
- nanoidp-1.0.0/README.md +524 -0
- nanoidp-1.0.0/config/settings.yaml +39 -0
- nanoidp-1.0.0/config/users.yaml +99 -0
- nanoidp-1.0.0/pyproject.toml +105 -0
- nanoidp-1.0.0/src/nanoidp/__init__.py +15 -0
- nanoidp-1.0.0/src/nanoidp/__main__.py +221 -0
- nanoidp-1.0.0/src/nanoidp/app.py +149 -0
- nanoidp-1.0.0/src/nanoidp/config.py +442 -0
- nanoidp-1.0.0/src/nanoidp/exceptions.py +196 -0
- nanoidp-1.0.0/src/nanoidp/mcp_server.py +820 -0
- nanoidp-1.0.0/src/nanoidp/routes/__init__.py +8 -0
- nanoidp-1.0.0/src/nanoidp/routes/api.py +199 -0
- nanoidp-1.0.0/src/nanoidp/routes/oauth.py +1118 -0
- nanoidp-1.0.0/src/nanoidp/routes/saml.py +597 -0
- nanoidp-1.0.0/src/nanoidp/routes/ui.py +790 -0
- nanoidp-1.0.0/src/nanoidp/services/__init__.py +22 -0
- nanoidp-1.0.0/src/nanoidp/services/audit.py +179 -0
- nanoidp-1.0.0/src/nanoidp/services/auth_code.py +203 -0
- nanoidp-1.0.0/src/nanoidp/services/crypto.py +483 -0
- nanoidp-1.0.0/src/nanoidp/services/token.py +133 -0
- nanoidp-1.0.0/src/nanoidp/services/yaml_writer.py +281 -0
- nanoidp-1.0.0/src/nanoidp/templates/audit.html +220 -0
- nanoidp-1.0.0/src/nanoidp/templates/authorize.html +126 -0
- nanoidp-1.0.0/src/nanoidp/templates/base.html +399 -0
- nanoidp-1.0.0/src/nanoidp/templates/claims.html +241 -0
- nanoidp-1.0.0/src/nanoidp/templates/clients.html +77 -0
- nanoidp-1.0.0/src/nanoidp/templates/clients_form.html +118 -0
- nanoidp-1.0.0/src/nanoidp/templates/device.html +136 -0
- nanoidp-1.0.0/src/nanoidp/templates/index.html +217 -0
- nanoidp-1.0.0/src/nanoidp/templates/keys.html +187 -0
- nanoidp-1.0.0/src/nanoidp/templates/login.html +114 -0
- nanoidp-1.0.0/src/nanoidp/templates/logout.html +64 -0
- nanoidp-1.0.0/src/nanoidp/templates/settings.html +179 -0
- nanoidp-1.0.0/src/nanoidp/templates/test.html +604 -0
- nanoidp-1.0.0/src/nanoidp/templates/user_detail.html +212 -0
- nanoidp-1.0.0/src/nanoidp/templates/users.html +203 -0
- nanoidp-1.0.0/src/nanoidp/templates/users_form.html +252 -0
- nanoidp-1.0.0/src/nanoidp/wizard.py +248 -0
nanoidp-1.0.0/.gitignore
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
ENV/
|
|
10
|
+
.pypirc
|
|
11
|
+
|
|
12
|
+
# IDE
|
|
13
|
+
.idea/
|
|
14
|
+
.vscode/
|
|
15
|
+
*.swp
|
|
16
|
+
*.swo
|
|
17
|
+
|
|
18
|
+
# Testing
|
|
19
|
+
.pytest_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
htmlcov/
|
|
22
|
+
|
|
23
|
+
# OS
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
26
|
+
|
|
27
|
+
# Keys (auto-generated, should not be committed)
|
|
28
|
+
keys/
|
|
29
|
+
|
|
30
|
+
# Old/backup files
|
|
31
|
+
*.backup
|
|
32
|
+
*.bak
|
|
33
|
+
|
|
34
|
+
# Build artifacts
|
|
35
|
+
dist/
|
|
36
|
+
build/
|
|
37
|
+
*.egg-info/
|
|
38
|
+
*.egg
|
|
39
|
+
|
|
40
|
+
# Local config overrides
|
|
41
|
+
config/*.local.yaml
|
nanoidp-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 NanoIDP 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.
|
nanoidp-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nanoidp
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A lightweight, configurable Identity Provider for development and testing
|
|
5
|
+
Project-URL: Homepage, https://github.com/cdelmonte-zg/nanoidp
|
|
6
|
+
Project-URL: Documentation, https://github.com/cdelmonte-zg/nanoidp#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/cdelmonte-zg/nanoidp.git
|
|
8
|
+
Project-URL: Issues, https://github.com/cdelmonte-zg/nanoidp/issues
|
|
9
|
+
Author: Christian Del Monte
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: authentication,development,identity-provider,idp,mock,oauth2,oidc,saml,testing
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Framework :: Flask
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
|
|
24
|
+
Classifier: Topic :: Security
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: bcrypt>=4.0.0
|
|
28
|
+
Requires-Dist: cryptography>=41.0.0
|
|
29
|
+
Requires-Dist: flask-cors>=4.0.0
|
|
30
|
+
Requires-Dist: flask-limiter>=3.0.0
|
|
31
|
+
Requires-Dist: flask>=3.0.0
|
|
32
|
+
Requires-Dist: lxml>=5.0.0
|
|
33
|
+
Requires-Dist: mcp>=1.0.0
|
|
34
|
+
Requires-Dist: pydantic>=2.0.0
|
|
35
|
+
Requires-Dist: pyjwt>=2.8.0
|
|
36
|
+
Requires-Dist: pyyaml>=6.0
|
|
37
|
+
Requires-Dist: signxml>=3.2.0
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: black>=23.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# NanoIDP
|
|
46
|
+
|
|
47
|
+
A lightweight, configurable Identity Provider for development and testing. Supports OAuth2/OIDC and SAML 2.0 protocols with a full-featured web UI for configuration.
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- **OAuth2 / OIDC** - Full OAuth2 implementation with Authorization Code, Password, Client Credentials, Refresh Token, and Device Authorization grants
|
|
52
|
+
- **PKCE Support** - Proof Key for Code Exchange (RFC 7636) with S256 and plain methods
|
|
53
|
+
- **Token Management** - Introspection (RFC 7662) and Revocation (RFC 7009) endpoints
|
|
54
|
+
- **OIDC Logout** - End Session endpoint for RP-initiated logout
|
|
55
|
+
- **Device Flow** - Device Authorization Grant (RFC 8628) for CLI/IoT applications
|
|
56
|
+
- **SAML 2.0** - SSO and AttributeQuery endpoints with signed assertions
|
|
57
|
+
- **MCP Server** - Model Context Protocol integration for Claude Code
|
|
58
|
+
- **Web UI** - Full configuration interface for users, clients, settings, and more
|
|
59
|
+
- **YAML Configuration** - File-based configuration, no database required
|
|
60
|
+
- **Attribute-based Access Control** - Flexible authority prefixes and claims mapping
|
|
61
|
+
- **Audit Logging** - Track all authentication events
|
|
62
|
+
- **Docker Support** - Ready to deploy with Docker/Docker Compose
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
### From PyPI
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install nanoidp
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### From Source
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/cdelmonte-zg/nanoidp.git
|
|
76
|
+
cd nanoidp
|
|
77
|
+
pip install .
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### For Development
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/cdelmonte-zg/nanoidp.git
|
|
84
|
+
cd nanoidp
|
|
85
|
+
pip install -e ".[dev]"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Quick Start
|
|
89
|
+
|
|
90
|
+
### Initialize Configuration (Recommended for pip install)
|
|
91
|
+
|
|
92
|
+
When installing via pip, use the `init` command to create a configuration directory:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Create config in ./config (default)
|
|
96
|
+
python -m nanoidp init
|
|
97
|
+
|
|
98
|
+
# Or specify a custom path
|
|
99
|
+
python -m nanoidp init ./my-idp-config
|
|
100
|
+
|
|
101
|
+
# Then start with that config
|
|
102
|
+
python -m nanoidp --config ./my-idp-config
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
This creates:
|
|
106
|
+
- `users.yaml` - User definitions
|
|
107
|
+
- `settings.yaml` - OAuth/SAML settings
|
|
108
|
+
- `keys/` - Directory for RSA keys (auto-generated on startup)
|
|
109
|
+
|
|
110
|
+
### Interactive Wizard
|
|
111
|
+
|
|
112
|
+
For a guided setup:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python -m nanoidp wizard
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The wizard guides you through:
|
|
119
|
+
- Server configuration (host, port, issuer)
|
|
120
|
+
- OAuth client setup
|
|
121
|
+
- Admin user creation
|
|
122
|
+
- Token settings
|
|
123
|
+
|
|
124
|
+
### Run the Server
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Run with default config (./config)
|
|
128
|
+
python -m nanoidp
|
|
129
|
+
|
|
130
|
+
# With custom config directory
|
|
131
|
+
python -m nanoidp --config /path/to/config
|
|
132
|
+
|
|
133
|
+
# Using environment variable
|
|
134
|
+
NANOIDP_CONFIG_DIR=/path/to/config python -m nanoidp
|
|
135
|
+
|
|
136
|
+
# With options
|
|
137
|
+
python -m nanoidp --port 8080 --debug
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Using Docker
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
docker-compose up -d
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The server will be available at `http://localhost:8000`
|
|
147
|
+
|
|
148
|
+
## Web Interface
|
|
149
|
+
|
|
150
|
+
Access the admin UI at `http://localhost:8000`:
|
|
151
|
+
|
|
152
|
+
- **Dashboard** - Overview and quick stats
|
|
153
|
+
- **Users** - Create, edit, delete users
|
|
154
|
+
- **OAuth Clients** - Manage OAuth2 client credentials
|
|
155
|
+
- **Settings** - Configure IdP settings (issuer, audience, SAML)
|
|
156
|
+
- **Keys & Certs** - View and regenerate RSA keys
|
|
157
|
+
- **Claims** - Configure authority prefix mappings
|
|
158
|
+
- **Audit Log** - View and export authentication events
|
|
159
|
+
- **Token Tester** - Generate and inspect tokens
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
### Users (`config/users.yaml`)
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
users:
|
|
167
|
+
admin:
|
|
168
|
+
password: "admin"
|
|
169
|
+
email: "admin@example.org"
|
|
170
|
+
identity_class: "INTERNAL"
|
|
171
|
+
entitlements:
|
|
172
|
+
- "ADMIN_ACCESS"
|
|
173
|
+
- "USER_MANAGEMENT"
|
|
174
|
+
roles:
|
|
175
|
+
- "USER"
|
|
176
|
+
- "ADMIN"
|
|
177
|
+
tenant: "default"
|
|
178
|
+
source_acl:
|
|
179
|
+
- "ACL_READ"
|
|
180
|
+
- "ACL_WRITE"
|
|
181
|
+
|
|
182
|
+
default_user: "admin"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Settings (`config/settings.yaml`)
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
server:
|
|
189
|
+
host: "0.0.0.0"
|
|
190
|
+
port: 8000
|
|
191
|
+
|
|
192
|
+
oauth:
|
|
193
|
+
issuer: "http://localhost:8000"
|
|
194
|
+
audience: "my-app"
|
|
195
|
+
token_expiry_minutes: 60
|
|
196
|
+
clients:
|
|
197
|
+
- client_id: "demo-client"
|
|
198
|
+
client_secret: "demo-secret"
|
|
199
|
+
description: "Default demo client"
|
|
200
|
+
|
|
201
|
+
saml:
|
|
202
|
+
entity_id: "http://localhost:8000/saml"
|
|
203
|
+
sso_url: "http://localhost:8000/saml/sso"
|
|
204
|
+
default_acs_url: "http://localhost:8080/login/saml2/sso/nanoidp"
|
|
205
|
+
|
|
206
|
+
authority_prefixes:
|
|
207
|
+
roles: "ROLE_"
|
|
208
|
+
identity_class: "IDENTITY_"
|
|
209
|
+
entitlements: "ENT_"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## API Endpoints
|
|
213
|
+
|
|
214
|
+
### OAuth2 / OIDC
|
|
215
|
+
|
|
216
|
+
| Endpoint | Description |
|
|
217
|
+
|----------|-------------|
|
|
218
|
+
| `GET /.well-known/openid-configuration` | OIDC Discovery |
|
|
219
|
+
| `GET /.well-known/jwks.json` | JSON Web Key Set |
|
|
220
|
+
| `GET /authorize` | Authorization endpoint (login page) |
|
|
221
|
+
| `POST /token` | Token endpoint |
|
|
222
|
+
| `GET/POST /userinfo` | UserInfo endpoint |
|
|
223
|
+
| `POST /introspect` | Token Introspection (RFC 7662) |
|
|
224
|
+
| `POST /revoke` | Token Revocation (RFC 7009) |
|
|
225
|
+
| `GET/POST /logout` | OIDC End Session / Logout |
|
|
226
|
+
| `POST /device_authorization` | Device Authorization (RFC 8628) |
|
|
227
|
+
| `GET/POST /device` | Device verification page |
|
|
228
|
+
|
|
229
|
+
### SAML
|
|
230
|
+
|
|
231
|
+
| Endpoint | Description |
|
|
232
|
+
|----------|-------------|
|
|
233
|
+
| `GET /saml/metadata` | IdP Metadata |
|
|
234
|
+
| `POST /saml/sso` | Single Sign-On |
|
|
235
|
+
| `POST /saml/attribute-query` | Attribute Query |
|
|
236
|
+
|
|
237
|
+
### REST API
|
|
238
|
+
|
|
239
|
+
| Endpoint | Description |
|
|
240
|
+
|----------|-------------|
|
|
241
|
+
| `GET /api/health` | Health check |
|
|
242
|
+
| `GET /api/users` | List users |
|
|
243
|
+
| `GET /api/users/{username}` | Get user details |
|
|
244
|
+
| `POST /api/users/{username}/token` | Generate token |
|
|
245
|
+
| `GET /api/audit` | Get audit log |
|
|
246
|
+
| `POST /api/config/reload` | Reload configuration |
|
|
247
|
+
| `POST /api/keys/rotate` | Rotate cryptographic keys |
|
|
248
|
+
| `GET /api/keys/info` | Get key information |
|
|
249
|
+
|
|
250
|
+
## Token Request Examples
|
|
251
|
+
|
|
252
|
+
### Password Grant
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
curl -X POST 'http://localhost:8000/token' \
|
|
256
|
+
-u 'demo-client:demo-secret' \
|
|
257
|
+
-d 'grant_type=password&username=admin&password=admin'
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Client Credentials Grant
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
curl -X POST 'http://localhost:8000/token' \
|
|
264
|
+
-u 'demo-client:demo-secret' \
|
|
265
|
+
-d 'grant_type=client_credentials'
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Refresh Token
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
curl -X POST 'http://localhost:8000/token' \
|
|
272
|
+
-u 'demo-client:demo-secret' \
|
|
273
|
+
-d 'grant_type=refresh_token&refresh_token=YOUR_REFRESH_TOKEN'
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Device Authorization Flow
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
# 1. Request device code
|
|
280
|
+
curl -X POST 'http://localhost:8000/device_authorization' \
|
|
281
|
+
-u 'demo-client:demo-secret' \
|
|
282
|
+
-d 'scope=openid'
|
|
283
|
+
|
|
284
|
+
# Response:
|
|
285
|
+
# {
|
|
286
|
+
# "device_code": "...",
|
|
287
|
+
# "user_code": "ABCD1234",
|
|
288
|
+
# "verification_uri": "http://localhost:8000/device",
|
|
289
|
+
# "expires_in": 600
|
|
290
|
+
# }
|
|
291
|
+
|
|
292
|
+
# 2. User visits verification_uri and enters user_code
|
|
293
|
+
|
|
294
|
+
# 3. Poll for token
|
|
295
|
+
curl -X POST 'http://localhost:8000/token' \
|
|
296
|
+
-u 'demo-client:demo-secret' \
|
|
297
|
+
-d 'grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=YOUR_DEVICE_CODE'
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Token Introspection
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
curl -X POST 'http://localhost:8000/introspect' \
|
|
304
|
+
-u 'demo-client:demo-secret' \
|
|
305
|
+
-d 'token=YOUR_ACCESS_TOKEN'
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Token Revocation
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
curl -X POST 'http://localhost:8000/revoke' \
|
|
312
|
+
-u 'demo-client:demo-secret' \
|
|
313
|
+
-d 'token=YOUR_ACCESS_TOKEN'
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## JWT Token Structure
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"iss": "http://localhost:8000",
|
|
321
|
+
"sub": "admin",
|
|
322
|
+
"aud": "my-app",
|
|
323
|
+
"iat": 1704100000,
|
|
324
|
+
"exp": 1704103600,
|
|
325
|
+
"roles": ["USER", "ADMIN"],
|
|
326
|
+
"tenant": "default",
|
|
327
|
+
"identity_class": "INTERNAL",
|
|
328
|
+
"entitlements": ["ADMIN_ACCESS", "USER_MANAGEMENT"],
|
|
329
|
+
"authorities": [
|
|
330
|
+
"ROLE_USER",
|
|
331
|
+
"ROLE_ADMIN",
|
|
332
|
+
"IDENTITY_INTERNAL",
|
|
333
|
+
"ENT_ADMIN_ACCESS",
|
|
334
|
+
"ENT_USER_MANAGEMENT",
|
|
335
|
+
"ACL_READ",
|
|
336
|
+
"ACL_WRITE"
|
|
337
|
+
]
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Development
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
# Install dev dependencies
|
|
345
|
+
pip install -e ".[dev]"
|
|
346
|
+
|
|
347
|
+
# Run tests
|
|
348
|
+
pytest
|
|
349
|
+
|
|
350
|
+
# Run in development mode
|
|
351
|
+
python -m nanoidp --debug
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## MCP Server (Model Context Protocol)
|
|
355
|
+
|
|
356
|
+
NanoIDP includes an MCP server for integration with Claude Code and other MCP-compatible tools.
|
|
357
|
+
|
|
358
|
+
### Available Tools
|
|
359
|
+
|
|
360
|
+
| Tool | Description |
|
|
361
|
+
|------|-------------|
|
|
362
|
+
| `list_users` | List all configured users |
|
|
363
|
+
| `get_user` | Get details of a specific user |
|
|
364
|
+
| `create_user` | Create a new user |
|
|
365
|
+
| `delete_user` | Delete a user |
|
|
366
|
+
| `generate_token` | Generate OAuth2 access token for a user |
|
|
367
|
+
| `decode_token` | Decode JWT token (without verification) |
|
|
368
|
+
| `verify_token` | Verify JWT token signature and expiration |
|
|
369
|
+
| `list_clients` | List OAuth clients |
|
|
370
|
+
| `get_client` | Get client details |
|
|
371
|
+
| `get_settings` | Get current IdP settings |
|
|
372
|
+
| `reload_config` | Reload configuration from files |
|
|
373
|
+
| `get_oidc_discovery` | Get OIDC discovery document |
|
|
374
|
+
| `get_jwks` | Get JSON Web Key Set |
|
|
375
|
+
|
|
376
|
+
### Claude Code CLI Configuration
|
|
377
|
+
|
|
378
|
+
Add to your project's `.claude/settings.json`:
|
|
379
|
+
|
|
380
|
+
```json
|
|
381
|
+
{
|
|
382
|
+
"mcpServers": {
|
|
383
|
+
"nanoidp": {
|
|
384
|
+
"command": "python",
|
|
385
|
+
"args": ["-m", "nanoidp.mcp_server"],
|
|
386
|
+
"env": {
|
|
387
|
+
"NANOIDP_CONFIG_DIR": "./config"
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Or if NanoIDP is installed globally:
|
|
395
|
+
|
|
396
|
+
```json
|
|
397
|
+
{
|
|
398
|
+
"mcpServers": {
|
|
399
|
+
"nanoidp": {
|
|
400
|
+
"command": "nanoidp-mcp",
|
|
401
|
+
"env": {
|
|
402
|
+
"NANOIDP_CONFIG_DIR": "/path/to/config"
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Claude Desktop Configuration
|
|
410
|
+
|
|
411
|
+
Add to `~/.claude/claude_desktop_config.json`:
|
|
412
|
+
|
|
413
|
+
```json
|
|
414
|
+
{
|
|
415
|
+
"mcpServers": {
|
|
416
|
+
"nanoidp": {
|
|
417
|
+
"command": "nanoidp-mcp",
|
|
418
|
+
"env": {
|
|
419
|
+
"NANOIDP_CONFIG_DIR": "/path/to/nanoidp/config"
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Running Standalone
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
# Run MCP server directly
|
|
430
|
+
python -m nanoidp.mcp_server
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Security
|
|
434
|
+
|
|
435
|
+
This is a **development/testing tool** and should NOT be used in production environments.
|
|
436
|
+
|
|
437
|
+
### Security Profiles
|
|
438
|
+
|
|
439
|
+
NanoIDP supports two security profiles:
|
|
440
|
+
|
|
441
|
+
| Profile | Description |
|
|
442
|
+
|---------|-------------|
|
|
443
|
+
| `dev` (default) | Maximum convenience for development: plaintext passwords, permissive CORS, no rate limiting |
|
|
444
|
+
| `stricter-dev` | Semi-hardened mode: bcrypt passwords, restricted CORS, rate limiting, debug mode blocked |
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
# Run with stricter-dev profile
|
|
448
|
+
python -m nanoidp --profile stricter-dev
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**Feature comparison:**
|
|
452
|
+
|
|
453
|
+
| Feature | `dev` | `stricter-dev` |
|
|
454
|
+
|---------|-------|----------------|
|
|
455
|
+
| Password storage | Plaintext | bcrypt hash |
|
|
456
|
+
| CORS | `*` (all origins) | localhost only |
|
|
457
|
+
| Rate limiting | None | 10 req/min on `/token` |
|
|
458
|
+
| Debug mode | Allowed | Blocked |
|
|
459
|
+
|
|
460
|
+
### Key Management
|
|
461
|
+
|
|
462
|
+
#### External Keys
|
|
463
|
+
|
|
464
|
+
You can use your own RSA keys instead of auto-generated ones:
|
|
465
|
+
|
|
466
|
+
```yaml
|
|
467
|
+
# settings.yaml
|
|
468
|
+
jwt:
|
|
469
|
+
external_keys:
|
|
470
|
+
private_key: /path/to/private.pem
|
|
471
|
+
public_key: /path/to/public.pem
|
|
472
|
+
kid: "my-custom-key-id"
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
#### Key Rotation
|
|
476
|
+
|
|
477
|
+
NanoIDP supports key rotation with multiple keys in JWKS for seamless token validation during rotation:
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
# Rotate keys via API
|
|
481
|
+
curl -X POST http://localhost:8000/api/keys/rotate
|
|
482
|
+
|
|
483
|
+
# Get key information
|
|
484
|
+
curl http://localhost:8000/api/keys/info
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
The JWKS endpoint (`/.well-known/jwks.json`) returns both the active key and previous keys (up to `max_previous_keys`, default 2) for validation of tokens signed with older keys.
|
|
488
|
+
|
|
489
|
+
### MCP Server Security
|
|
490
|
+
|
|
491
|
+
The MCP server exposes powerful administrative tools and should ONLY be used:
|
|
492
|
+
- Locally on developer machines
|
|
493
|
+
- In isolated development environments
|
|
494
|
+
- Never exposed to network access
|
|
495
|
+
|
|
496
|
+
**Mutating tools** (those that modify configuration):
|
|
497
|
+
- `create_user`, `update_user`, `delete_user`
|
|
498
|
+
- `create_client`, `update_client`, `delete_client`
|
|
499
|
+
- `generate_token`, `update_settings`, `save_config`
|
|
500
|
+
|
|
501
|
+
#### Admin Secret Protection
|
|
502
|
+
|
|
503
|
+
When `NANOIDP_MCP_ADMIN_SECRET` is set, mutating operations require the secret:
|
|
504
|
+
|
|
505
|
+
```json
|
|
506
|
+
{
|
|
507
|
+
"mcpServers": {
|
|
508
|
+
"nanoidp": {
|
|
509
|
+
"command": "nanoidp-mcp",
|
|
510
|
+
"env": {
|
|
511
|
+
"NANOIDP_CONFIG_DIR": "./config",
|
|
512
|
+
"NANOIDP_MCP_ADMIN_SECRET": "your-secret-here"
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
#### Readonly Mode
|
|
520
|
+
|
|
521
|
+
To completely disable mutating tools (create, update, delete, generate):
|
|
522
|
+
|
|
523
|
+
```bash
|
|
524
|
+
# Via CLI flag
|
|
525
|
+
nanoidp-mcp --readonly
|
|
526
|
+
|
|
527
|
+
# Via environment variable
|
|
528
|
+
NANOIDP_MCP_READONLY=true nanoidp-mcp
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
In Claude Code settings:
|
|
532
|
+
|
|
533
|
+
```json
|
|
534
|
+
{
|
|
535
|
+
"mcpServers": {
|
|
536
|
+
"nanoidp": {
|
|
537
|
+
"command": "nanoidp-mcp",
|
|
538
|
+
"args": ["--readonly"],
|
|
539
|
+
"env": {
|
|
540
|
+
"NANOIDP_CONFIG_DIR": "./config"
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Use readonly mode when you only need introspection (listing users, decoding tokens, viewing settings) but want to prevent accidental modifications.
|
|
548
|
+
|
|
549
|
+
All MCP tool calls are logged to the audit log.
|
|
550
|
+
|
|
551
|
+
For detailed usage examples with Claude Code, see [docs/MCP_WORKFLOW.md](docs/MCP_WORKFLOW.md).
|
|
552
|
+
|
|
553
|
+
### Environment Variables
|
|
554
|
+
|
|
555
|
+
| Variable | Description | Default |
|
|
556
|
+
|----------|-------------|---------|
|
|
557
|
+
| `NANOIDP_CONFIG_DIR` | Configuration directory | `./config` |
|
|
558
|
+
| `NANOIDP_MCP_ADMIN_SECRET` | Secret required for mutating MCP operations | (none) |
|
|
559
|
+
| `NANOIDP_MCP_READONLY` | Disable mutating MCP tools when set to `true` | `false` |
|
|
560
|
+
| `PORT` | Server port | `8000` |
|
|
561
|
+
|
|
562
|
+
## License
|
|
563
|
+
|
|
564
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
565
|
+
|
|
566
|
+
## Contributing
|
|
567
|
+
|
|
568
|
+
Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|