ldap-rest 0.1.3 → 0.1.5
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.
- package/README.md +31 -21
- package/dist/abstract/ldapFlat.js +1 -1
- package/dist/lib/ldapActions.js +1 -1
- package/dist/plugins/ldap/groups.js +1 -1
- package/dist/src/abstract/ldapFlat.d.ts +9 -0
- package/dist/src/browser/ldap-user-editor/components/UserTree.d.ts +2 -1
- package/dist/src/browser/shared/components/DisposableComponent.d.ts +29 -0
- package/dist/src/browser/shared/components/Modal.d.ts +6 -1
- package/dist/src/lib/ldapActions.d.ts +9 -0
- package/dist/src/plugins/ldap/groups.d.ts +9 -0
- package/docs/README.md +23 -486
- package/docs/{DEVELOPER_GUIDE.md → client-development/README.md} +24 -24
- package/docs/client-development/api/README.md +75 -0
- package/docs/client-development/browser/README.md +65 -0
- package/docs/{plugins/DEVELOPMENT.md → plugin-development/README.md} +4 -9
- package/docs/plugin-development/hooks.md +18 -0
- package/docs/usage/README.md +89 -0
- package/docs/usage/configuration.md +375 -0
- package/docs/usage/plugins/README.md +87 -0
- package/docs/usage/plugins/auth/README.md +65 -0
- package/docs/usage/plugins/auth/crowdsec.md +95 -0
- package/docs/usage/plugins/auth/hmac.md +293 -0
- package/docs/usage/plugins/auth/llng.md +99 -0
- package/docs/usage/plugins/auth/oidc.md +167 -0
- package/docs/usage/plugins/auth/rate-limit.md +62 -0
- package/docs/usage/plugins/auth/token.md +136 -0
- package/docs/usage/plugins/auth/totp.md +213 -0
- package/docs/usage/plugins/auth/trusted-proxy.md +127 -0
- package/docs/usage/plugins/integrations/README.md +42 -0
- package/docs/usage/plugins/ldap/README.md +79 -0
- package/docs/usage/troubleshooting.md +105 -0
- package/package.json +9 -1
- package/static/browser/ldap-user-editor/components/UserTree.js +6 -3
- package/static/browser/ldap-user-editor/components/UserTree.js.map +1 -1
- package/static/browser/ldap-user-editor.esm.js +62 -3
- package/static/browser/ldap-user-editor.esm.js.map +1 -1
- package/static/browser/ldap-user-editor.js +62 -3
- package/static/browser/ldap-user-editor.js.map +1 -1
- package/static/browser/ldap-user-editor.min.js +1 -1
- package/static/browser/ldap-user-editor.min.js.map +1 -1
- package/static/browser/shared/components/DisposableComponent.js +59 -0
- package/static/browser/shared/components/DisposableComponent.js.map +1 -0
- package/static/browser/shared/components/Modal.js +16 -6
- package/static/browser/shared/components/Modal.js.map +1 -1
- package/static/browser/shared.esm.js +72 -6
- package/static/browser/shared.esm.js.map +1 -1
- package/static/browser/shared.js +72 -6
- package/static/browser/shared.js.map +1 -1
- package/static/browser/shared.min.js +1 -1
- package/static/browser/shared.min.js.map +1 -1
- package/docs/authentication.md +0 -1194
- /package/docs/{OPENAPI.md → client-development/api/openapi.md} +0 -0
- /package/docs/{api/REFERENCE.md → client-development/api/reference.md} +0 -0
- /package/docs/{api/REST_API.md → client-development/api/rest-api.md} +0 -0
- /package/docs/{browser/LIBRARIES.md → client-development/browser/libraries.md} +0 -0
- /package/docs/{examples/EXAMPLES.md → client-development/examples/README.md} +0 -0
- /package/docs/{examples → client-development/examples/ldif}/twake-mailbox-type-nomenclature.ldif +0 -0
- /package/docs/{examples → client-development/examples/ldif}/twake-mailbox-type-schema-amendment.ldif +0 -0
- /package/docs/{schemas/SCHEMAS.md → client-development/schemas/README.md} +0 -0
- /package/docs/{PLUGIN_DEPENDENCIES.md → plugin-development/dependencies.md} +0 -0
- /package/docs/{testing-with-embedded-ldap.md → plugin-development/testing.md} +0 -0
- /package/docs/{presenterm.md → presentations/en.md} +0 -0
- /package/docs/{presenterm-fr.md → presentations/fr.md} +0 -0
- /package/docs/{authzLinid1.md → usage/plugins/auth/authz-linid1.md} +0 -0
- /package/docs/{authzPerBranch.md → usage/plugins/auth/authz-per-branch.md} +0 -0
- /package/docs/{twakeAppAccountsApi.md → usage/plugins/integrations/app-accounts.md} +0 -0
- /package/docs/{twakeCalendarResources.md → usage/plugins/integrations/calendar-resources.md} +0 -0
- /package/docs/{twakeJames.md → usage/plugins/integrations/james-mail.md} +0 -0
- /package/docs/{jamesTeamMailboxes.md → usage/plugins/integrations/james-mailboxes.md} +0 -0
- /package/docs/{ldapBulkImport.md → usage/plugins/ldap/bulk-import.md} +0 -0
- /package/docs/{ldapExternalUsersInGroups.md → usage/plugins/ldap/external-users.md} +0 -0
- /package/docs/{ldapFlatGeneric.md → usage/plugins/ldap/flat-generic.md} +0 -0
- /package/docs/{ldapGroups.md → usage/plugins/ldap/groups.md} +0 -0
- /package/docs/{onChange.md → usage/plugins/ldap/on-change.md} +0 -0
- /package/docs/{ldapOrganizations.md → usage/plugins/ldap/organizations.md} +0 -0
- /package/docs/{ldapTrash.md → usage/plugins/ldap/trash.md} +0 -0
- /package/docs/{static.md → usage/plugins/utilities/static.md} +0 -0
- /package/docs/{weblogs.md → usage/plugins/utilities/weblogs.md} +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# OpenID Connect Authentication
|
|
2
|
+
|
|
3
|
+
OAuth 2.0 / OpenID Connect authentication for modern identity providers.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
--plugin core/auth/openidconnect \
|
|
9
|
+
--oidc-server "https://auth.example.com" \
|
|
10
|
+
--oidc-client-id "ldap-rest-client" \
|
|
11
|
+
--oidc-client-secret "client-secret-here" \
|
|
12
|
+
--base-url "https://api.example.com"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Environment Variables:**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
DM_OIDC_SERVER="https://auth.example.com"
|
|
19
|
+
DM_OIDC_CLIENT_ID="ldap-rest-client"
|
|
20
|
+
DM_OIDC_CLIENT_SECRET="client-secret-here"
|
|
21
|
+
DM_BASE_URL="https://api.example.com"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
|
|
26
|
+
1. **OpenID Provider**: [Lemonldap-NG](https://lemonldap-ng.org), Keycloak, Auth0, Okta, Azure AD, etc.
|
|
27
|
+
2. **Client Registration**: LDAP-Rest registered as OAuth2/OIDC client
|
|
28
|
+
3. **Optional Dependency**: The `express-openid-connect` npm package
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
Install the optional dependency:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install express-openid-connect
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## How It Works
|
|
39
|
+
|
|
40
|
+
1. Uses `express-openid-connect` for OAuth2/OIDC flow
|
|
41
|
+
2. Handles authorization code flow automatically
|
|
42
|
+
3. Extracts user identity from `sub` claim
|
|
43
|
+
4. Sets `req.user` to user's subject identifier
|
|
44
|
+
5. Provides `beforeAuth` and `afterAuth` hooks for customization
|
|
45
|
+
|
|
46
|
+
## Provider Configuration
|
|
47
|
+
|
|
48
|
+
### Keycloak
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
--oidc-server "https://keycloak.example.com/realms/master" \
|
|
52
|
+
--oidc-client-id "ldap-rest" \
|
|
53
|
+
--oidc-client-secret "abc123..." \
|
|
54
|
+
--base-url "https://api.example.com"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Client Settings:**
|
|
58
|
+
|
|
59
|
+
- Access Type: `confidential`
|
|
60
|
+
- Valid Redirect URIs: `https://api.example.com/callback`
|
|
61
|
+
- Standard Flow Enabled: `ON`
|
|
62
|
+
|
|
63
|
+
### Auth0
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
--oidc-server "https://tenant.auth0.com" \
|
|
67
|
+
--oidc-client-id "your-client-id" \
|
|
68
|
+
--oidc-client-secret "your-client-secret" \
|
|
69
|
+
--base-url "https://api.example.com"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Azure AD
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
--oidc-server "https://login.microsoftonline.com/{tenant-id}/v2.0" \
|
|
76
|
+
--oidc-client-id "application-id" \
|
|
77
|
+
--oidc-client-secret "client-secret" \
|
|
78
|
+
--base-url "https://api.example.com"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Scopes
|
|
82
|
+
|
|
83
|
+
Default scopes requested:
|
|
84
|
+
|
|
85
|
+
- `openid` - Basic OpenID Connect
|
|
86
|
+
- `profile` - User profile information
|
|
87
|
+
- `email` - User email address
|
|
88
|
+
|
|
89
|
+
## Hooks
|
|
90
|
+
|
|
91
|
+
The OpenID Connect plugin supports custom hooks:
|
|
92
|
+
|
|
93
|
+
### beforeAuth Hook
|
|
94
|
+
|
|
95
|
+
Called before authentication processing:
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
hooks: {
|
|
99
|
+
beforeAuth: async ([req, res]) => {
|
|
100
|
+
// Custom pre-authentication logic
|
|
101
|
+
console.log('Authentication attempt from:', req.ip);
|
|
102
|
+
return [req, res];
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### afterAuth Hook
|
|
108
|
+
|
|
109
|
+
Called after successful authentication:
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
hooks: {
|
|
113
|
+
afterAuth: async ([req, res]) => {
|
|
114
|
+
// Access OIDC user data
|
|
115
|
+
const user = req.oidc.user;
|
|
116
|
+
console.log('User authenticated:', user.email);
|
|
117
|
+
|
|
118
|
+
// Add custom user properties
|
|
119
|
+
req.customData = {
|
|
120
|
+
email: user.email,
|
|
121
|
+
name: user.name,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return [req, res];
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Use Cases
|
|
130
|
+
|
|
131
|
+
- **Modern Identity Providers**: Integration with cloud identity services
|
|
132
|
+
- **Social Login**: Google, Microsoft, GitHub authentication
|
|
133
|
+
- **Multi-Tenant Applications**: Different OIDC providers per tenant
|
|
134
|
+
- **Standards-Based**: Portable across OIDC-compliant providers
|
|
135
|
+
|
|
136
|
+
## Troubleshooting
|
|
137
|
+
|
|
138
|
+
**Problem:** express-openid-connect not found
|
|
139
|
+
|
|
140
|
+
**Solution:** Install optional dependency:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm install express-openid-connect
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Problem:** Missing config parameter
|
|
147
|
+
|
|
148
|
+
**Solution:** Ensure all required parameters are set:
|
|
149
|
+
|
|
150
|
+
- `--oidc-server`
|
|
151
|
+
- `--oidc-client-id`
|
|
152
|
+
- `--oidc-client-secret`
|
|
153
|
+
- `--base-url`
|
|
154
|
+
|
|
155
|
+
**Problem:** Redirect loop
|
|
156
|
+
|
|
157
|
+
**Solutions:**
|
|
158
|
+
|
|
159
|
+
1. Set a long secret, shorts are refused by `openid-client`
|
|
160
|
+
2. Verify `--base-url` matches actual public URL
|
|
161
|
+
3. Check redirect URI in provider matches `{base-url}/callback`
|
|
162
|
+
4. Ensure HTTPS in production (many providers require it)
|
|
163
|
+
|
|
164
|
+
## See Also
|
|
165
|
+
|
|
166
|
+
- [OpenID Connect Specification](https://openid.net/connect/)
|
|
167
|
+
- [express-openid-connect](https://github.com/auth0/express-openid-connect)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Rate Limiting
|
|
2
|
+
|
|
3
|
+
Prevent brute force attacks by limiting the number of requests per IP address.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
--plugin core/auth/rateLimit \
|
|
9
|
+
--rate-limit-window 60000 \
|
|
10
|
+
--rate-limit-max 100
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Environment Variables:**
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
DM_RATE_LIMIT_WINDOW=60000 # Time window in milliseconds (default: 60000 = 1 minute)
|
|
17
|
+
DM_RATE_LIMIT_MAX=100 # Maximum requests per window (default: 100)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How It Works
|
|
21
|
+
|
|
22
|
+
1. Tracks request count per IP address
|
|
23
|
+
2. Uses sliding window algorithm
|
|
24
|
+
3. Returns `429 Too Many Requests` when limit exceeded
|
|
25
|
+
4. Automatically respects `X-Forwarded-For` when behind trusted proxy
|
|
26
|
+
|
|
27
|
+
## Use Cases
|
|
28
|
+
|
|
29
|
+
- **Brute Force Prevention**: Limit authentication attempts
|
|
30
|
+
- **API Protection**: Prevent abuse of API endpoints
|
|
31
|
+
- **DDoS Mitigation**: Basic protection against flood attacks
|
|
32
|
+
- **Fair Usage**: Ensure equitable API access
|
|
33
|
+
|
|
34
|
+
## Headers
|
|
35
|
+
|
|
36
|
+
When rate limiting is active, responses include:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
X-RateLimit-Limit: 100
|
|
40
|
+
X-RateLimit-Remaining: 95
|
|
41
|
+
X-RateLimit-Reset: 1698765492
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Combining with Trusted Proxy
|
|
45
|
+
|
|
46
|
+
For correct IP detection behind reverse proxies:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
--plugin core/auth/trustedProxy \
|
|
50
|
+
--trusted-proxy "127.0.0.1" \
|
|
51
|
+
--plugin core/auth/rateLimit \
|
|
52
|
+
--rate-limit-max 100
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The rate limiter will use the real client IP from `X-Forwarded-For` when the request comes from a trusted proxy.
|
|
56
|
+
|
|
57
|
+
## Security Considerations
|
|
58
|
+
|
|
59
|
+
- Always use with `trustedProxy` plugin behind reverse proxies
|
|
60
|
+
- Consider different limits for different endpoints
|
|
61
|
+
- Monitor for legitimate users hitting limits
|
|
62
|
+
- Combine with CrowdSec for IP reputation
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Token Authentication
|
|
2
|
+
|
|
3
|
+
Simple stateless authentication using bearer tokens.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
### Basic Configuration
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
--plugin core/auth/token \
|
|
11
|
+
--auth-token "secret-token-1" \
|
|
12
|
+
--auth-token "secret-token-2" \
|
|
13
|
+
--auth-token "secret-token-3"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Environment Variable:**
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
DM_AUTH_TOKENS="token1,token2,token3"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Named Tokens (Recommended)
|
|
23
|
+
|
|
24
|
+
Associate a descriptive name with each token for better logging and audit trails:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
--plugin core/auth/token \
|
|
28
|
+
--auth-token "9f8e7d6c5b4a:web-application" \
|
|
29
|
+
--auth-token "1a2b3c4d5e6f:monitoring-service" \
|
|
30
|
+
--auth-token "f1e2d3c4b5a6:backup-scripts"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Environment Variable:**
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
DM_AUTH_TOKENS="9f8e7d6c5b4a:web-application,1a2b3c4d5e6f:monitoring-service,f1e2d3c4b5a6:backup-scripts"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Syntax:** `token:name`
|
|
40
|
+
|
|
41
|
+
- Token value comes first (the secret)
|
|
42
|
+
- Colon `:` separator
|
|
43
|
+
- Name comes second (descriptive identifier)
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
Include the token in the `Authorization` header:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
curl -H "Authorization: Bearer 9f8e7d6c5b4a" \
|
|
51
|
+
http://localhost:8081/api/v1/ldap/users
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## How It Works
|
|
55
|
+
|
|
56
|
+
1. Extracts token from `Authorization: Bearer <token>` header
|
|
57
|
+
2. Validates token against configured list
|
|
58
|
+
3. Sets `req.user` to the token's name (e.g., `"web-application"`) or `"token {index}"` for unnamed tokens
|
|
59
|
+
4. Returns 401 Unauthorized if token is missing or invalid
|
|
60
|
+
|
|
61
|
+
**Named tokens in logs:**
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
INFO: Request from user: web-application
|
|
65
|
+
INFO: Request from user: monitoring-service
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Unnamed tokens in logs (backward compatible):**
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
INFO: Request from user: token 0
|
|
72
|
+
INFO: Request from user: token 1
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Use Cases
|
|
76
|
+
|
|
77
|
+
- **Development/Testing**: Quick authentication without complex setup
|
|
78
|
+
- **Service-to-Service**: API access for backend services
|
|
79
|
+
- **CI/CD Pipelines**: Automated scripts and deployments
|
|
80
|
+
- **Simple Deployments**: Small teams without SSO infrastructure
|
|
81
|
+
|
|
82
|
+
## Security Considerations
|
|
83
|
+
|
|
84
|
+
- Tokens are static and shared (not user-specific)
|
|
85
|
+
- Use HTTPS in production to protect tokens in transit
|
|
86
|
+
- Rotate tokens regularly
|
|
87
|
+
- Limit token count to necessary services only
|
|
88
|
+
- Consider combining with IP whitelisting
|
|
89
|
+
|
|
90
|
+
## Example: Multiple Services with Named Tokens
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# API server configuration with named tokens
|
|
94
|
+
npx ldap-rest \
|
|
95
|
+
--plugin core/auth/token \
|
|
96
|
+
--auth-token "a1b2c3d4e5f6:production-web-app" \
|
|
97
|
+
--auth-token "f6e5d4c3b2a1:prometheus-monitoring" \
|
|
98
|
+
--auth-token "1234567890ab:nightly-backup" \
|
|
99
|
+
--ldap-url ldap://localhost:389 \
|
|
100
|
+
...
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Web application (logs will show: user: production-web-app)
|
|
105
|
+
curl -H "Authorization: Bearer a1b2c3d4e5f6" \
|
|
106
|
+
http://api/v1/ldap/users
|
|
107
|
+
|
|
108
|
+
# Monitoring system (logs will show: user: prometheus-monitoring)
|
|
109
|
+
curl -H "Authorization: Bearer f6e5d4c3b2a1" \
|
|
110
|
+
http://api/v1/ldap/groups
|
|
111
|
+
|
|
112
|
+
# Backup script (logs will show: user: nightly-backup)
|
|
113
|
+
curl -H "Authorization: Bearer 1234567890ab" \
|
|
114
|
+
http://api/v1/ldap/organizations/top
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Example: Mixed Named and Unnamed Tokens
|
|
118
|
+
|
|
119
|
+
You can mix named and unnamed tokens for backward compatibility:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
--auth-token "abc123:production-api" \
|
|
123
|
+
--auth-token "def456" # Unnamed, will be "token 1" in logs \
|
|
124
|
+
--auth-token "ghi789:staging-app"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Troubleshooting
|
|
128
|
+
|
|
129
|
+
**Problem:** 401 Unauthorized despite correct token
|
|
130
|
+
|
|
131
|
+
**Solutions:**
|
|
132
|
+
|
|
133
|
+
1. Ensure `Authorization: Bearer {token}` format (not just the token)
|
|
134
|
+
2. Check token is in configured list
|
|
135
|
+
3. Verify no whitespace in token
|
|
136
|
+
4. Check server logs for token mismatch
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# TOTP Authentication
|
|
2
|
+
|
|
3
|
+
Time-based One-Time Password (TOTP) authentication using dynamic codes compatible with authenticator apps.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
### Basic Configuration
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
--plugin core/auth/totp \
|
|
11
|
+
--auth-totp "JBSWY3DPEHPK3PXP:admin:6" \
|
|
12
|
+
--auth-totp "HXDMVJECJJWSRB3H:service:8"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Environment Variable:**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
DM_AUTH_TOTP="JBSWY3DPEHPK3PXP:admin:6,HXDMVJECJJWSRB3H:service:8"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Syntax:** `secret:name[:digits]`
|
|
22
|
+
|
|
23
|
+
- **secret**: Base32-encoded secret (e.g., generated by authenticator apps)
|
|
24
|
+
- **name**: Descriptive user/service name
|
|
25
|
+
- **digits**: Number of digits in TOTP code (optional, 6-10, default: 6)
|
|
26
|
+
|
|
27
|
+
### Advanced Configuration
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
--auth-totp-window 1 # Time window tolerance (±30s with step=30)
|
|
31
|
+
--auth-totp-step 30 # Time step in seconds (default: 30)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Environment Variables:**
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
DM_AUTH_TOTP_WINDOW=1 # Default: 1
|
|
38
|
+
DM_AUTH_TOTP_STEP=30 # Default: 30
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- **window**: Number of time steps to check before/after current time (compensates for clock drift)
|
|
42
|
+
- **step**: Time interval in seconds for code generation (typically 30)
|
|
43
|
+
|
|
44
|
+
## Generating Secrets
|
|
45
|
+
|
|
46
|
+
Use an authenticator app or generate Base32 secrets:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Using Node.js crypto (example)
|
|
50
|
+
node -e "console.log(require('crypto').randomBytes(20).toString('base32'))"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
### HTTP Requests
|
|
56
|
+
|
|
57
|
+
Include the current TOTP code in the `Authorization` header:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Get current TOTP code from your authenticator app
|
|
61
|
+
curl -H "Authorization: Bearer 123456" \
|
|
62
|
+
http://localhost:8081/api/v1/ldap/users
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Browser Client
|
|
66
|
+
|
|
67
|
+
The TOTP client library is available as an npm module export:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install ldap-rest
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import {
|
|
75
|
+
TotpAuthClient,
|
|
76
|
+
generateTotp,
|
|
77
|
+
getRemainingSeconds,
|
|
78
|
+
isValidBase32,
|
|
79
|
+
} from 'ldap-rest/browser-shared-utils-totp';
|
|
80
|
+
|
|
81
|
+
const client = new TotpAuthClient({
|
|
82
|
+
secret: 'JBSWY3DPEHPK3PXP',
|
|
83
|
+
digits: 6,
|
|
84
|
+
step: 30,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Automatic authentication with current TOTP code
|
|
88
|
+
const response = await client.get('/api/v1/ldap/users');
|
|
89
|
+
const users = await response.json();
|
|
90
|
+
|
|
91
|
+
// Or get current code manually
|
|
92
|
+
const code = await client.getCode();
|
|
93
|
+
console.log(`Current TOTP code: ${code}`);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Live Demo:** See [examples/web/totp-client.html](../../../../examples/web/totp-client.html) for an interactive demonstration.
|
|
97
|
+
|
|
98
|
+
## How It Works
|
|
99
|
+
|
|
100
|
+
1. Extracts TOTP code from `Authorization: Bearer <code>` header
|
|
101
|
+
2. Generates expected TOTP codes for current time window
|
|
102
|
+
3. Validates code against all configured users' secrets
|
|
103
|
+
4. Supports multiple time windows (±window × step seconds) for clock drift tolerance
|
|
104
|
+
5. Sets `req.user` to the user's name on successful validation
|
|
105
|
+
6. Returns 401 Unauthorized if code is missing, invalid, or expired
|
|
106
|
+
|
|
107
|
+
## Multi-User Example
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Configure multiple users with different code lengths
|
|
111
|
+
--plugin core/auth/totp \
|
|
112
|
+
--auth-totp "JBSWY3DPEHPK3PXP:admin:6" \
|
|
113
|
+
--auth-totp "HXDMVJECJJWSRB3H:api-service:8" \
|
|
114
|
+
--auth-totp "IXDMVJECJJWSRB2A:monitoring:10"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**In logs:**
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
INFO: TOTP authentication successful for user: admin
|
|
121
|
+
INFO: TOTP authentication successful for user: api-service
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Use Cases
|
|
125
|
+
|
|
126
|
+
- **Enhanced Security**: Dynamic codes that expire every 30 seconds
|
|
127
|
+
- **No Shared Secrets**: Each user/service has unique TOTP secret
|
|
128
|
+
- **Compatible**: Works with Google Authenticator, Authy, 1Password, etc.
|
|
129
|
+
- **API Access**: Suitable for automated scripts with TOTP generation
|
|
130
|
+
- **Multi-Factor**: Can be combined with other authentication methods
|
|
131
|
+
|
|
132
|
+
## Security Considerations
|
|
133
|
+
|
|
134
|
+
- **Secret Storage**: Store Base32 secrets securely (encrypted configuration, environment variables)
|
|
135
|
+
- **HTTPS Required**: Always use HTTPS to protect TOTP codes in transit
|
|
136
|
+
- **Clock Synchronization**: Ensure server and client clocks are synchronized (NTP recommended)
|
|
137
|
+
- **Window Size**: Larger windows are more tolerant but slightly less secure
|
|
138
|
+
- **Secret Rotation**: Rotate TOTP secrets periodically for better security
|
|
139
|
+
- **Rate Limiting**: Consider adding rate limiting to prevent brute force attacks
|
|
140
|
+
|
|
141
|
+
## Browser Client Features
|
|
142
|
+
|
|
143
|
+
The TOTP browser library provides these functions and classes:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import {
|
|
147
|
+
generateTotp,
|
|
148
|
+
getRemainingSeconds,
|
|
149
|
+
isValidBase32,
|
|
150
|
+
TotpAuthClient
|
|
151
|
+
} from 'ldap-rest/browser-shared-utils-totp';
|
|
152
|
+
|
|
153
|
+
// Generate TOTP code
|
|
154
|
+
const code = await generateTotp({
|
|
155
|
+
secret: 'JBSWY3DPEHPK3PXP',
|
|
156
|
+
digits: 6,
|
|
157
|
+
step: 30
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Check validity time
|
|
161
|
+
const remaining = getRemainingSeconds(30);
|
|
162
|
+
console.log(`Code expires in ${remaining} seconds`);
|
|
163
|
+
|
|
164
|
+
// Validate secret format
|
|
165
|
+
if (isValidBase32('JBSWY3DPEHPK3PXP')) {
|
|
166
|
+
console.log('Valid Base32 secret');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// HTTP client with automatic TOTP authentication
|
|
170
|
+
const client = new TotpAuthClient({
|
|
171
|
+
secret: 'JBSWY3DPEHPK3PXP',
|
|
172
|
+
digits: 6,
|
|
173
|
+
step: 30
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// All HTTP methods automatically include TOTP in Authorization header
|
|
177
|
+
await client.post('/api/v1/ldap/users', { uid: 'user1', ... });
|
|
178
|
+
await client.put('/api/v1/ldap/users/user1', { mail: 'new@example.com' });
|
|
179
|
+
await client.delete('/api/v1/ldap/users/user1');
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Module Export:** `ldap-rest/browser-shared-utils-totp`
|
|
183
|
+
|
|
184
|
+
**Live Demo:** Run the server and open [http://localhost:8081/static/examples/web/totp-client.html](http://localhost:8081/static/examples/web/totp-client.html)
|
|
185
|
+
|
|
186
|
+
## Troubleshooting
|
|
187
|
+
|
|
188
|
+
**Problem:** 401 Unauthorized despite correct code
|
|
189
|
+
|
|
190
|
+
**Solutions:**
|
|
191
|
+
|
|
192
|
+
1. Verify secret is correctly configured (Base32 format)
|
|
193
|
+
2. Check system clock synchronization (use NTP)
|
|
194
|
+
3. Increase `--auth-totp-window` if experiencing timing issues
|
|
195
|
+
4. Ensure code is used immediately (codes expire every 30s)
|
|
196
|
+
5. Verify `Authorization: Bearer {code}` format
|
|
197
|
+
|
|
198
|
+
**Problem:** Codes don't match authenticator app
|
|
199
|
+
|
|
200
|
+
**Solutions:**
|
|
201
|
+
|
|
202
|
+
1. Verify secret matches exactly (case-sensitive Base32)
|
|
203
|
+
2. Check `--auth-totp-step` matches app configuration (usually 30)
|
|
204
|
+
3. Ensure both server and device clocks are synchronized
|
|
205
|
+
4. Confirm correct number of digits configured
|
|
206
|
+
|
|
207
|
+
**Problem:** Invalid Base32 secret warning
|
|
208
|
+
|
|
209
|
+
**Solutions:**
|
|
210
|
+
|
|
211
|
+
1. Secret must contain only A-Z and 2-7 characters
|
|
212
|
+
2. Use proper Base32 encoding (not Base64)
|
|
213
|
+
3. Remove padding `=` characters if present (optional)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Trusted Proxy
|
|
2
|
+
|
|
3
|
+
When running behind a reverse proxy (nginx, Apache, HAProxy, etc.), client IP addresses are typically passed via `X-Forwarded-For` headers. The `trustedProxy` plugin validates these headers to prevent IP spoofing attacks.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
--plugin core/auth/trustedProxy \
|
|
9
|
+
--trusted-proxy "127.0.0.1" \
|
|
10
|
+
--trusted-proxy "10.0.0.0/8" \
|
|
11
|
+
--trusted-proxy "192.168.0.0/16"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**Environment Variables:**
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Comma-separated list of trusted proxy IPs or CIDR ranges
|
|
18
|
+
DM_TRUSTED_PROXIES="127.0.0.1,10.0.0.0/8,192.168.0.0/16,::1"
|
|
19
|
+
|
|
20
|
+
# Optional: Header name for authenticated user from proxy (default: Auth-User)
|
|
21
|
+
DM_TRUSTED_PROXY_AUTH_HEADER="Auth-User"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## How It Works
|
|
25
|
+
|
|
26
|
+
1. **Request arrives** from reverse proxy
|
|
27
|
+
2. **Plugin checks** if `req.socket.remoteAddress` matches a trusted proxy
|
|
28
|
+
3. **If trusted:**
|
|
29
|
+
- `X-Forwarded-For` header is preserved
|
|
30
|
+
- `Auth-User` header (or custom header) is extracted for logging
|
|
31
|
+
- Request is marked as `req.trustedProxy = true`
|
|
32
|
+
4. **If untrusted:**
|
|
33
|
+
- `X-Forwarded-For` header is **removed** to prevent IP spoofing
|
|
34
|
+
- Request is marked as `req.trustedProxy = false`
|
|
35
|
+
5. **Other plugins** (rate limiting, CrowdSec, weblogs) see sanitized headers
|
|
36
|
+
|
|
37
|
+
## Supported Formats
|
|
38
|
+
|
|
39
|
+
- **IPv4 addresses:** `192.168.1.1`
|
|
40
|
+
- **IPv6 addresses:** `::1`, `fe80::1`
|
|
41
|
+
- **CIDR ranges:** `10.0.0.0/8`, `192.168.0.0/16`, `2001:db8::/32`
|
|
42
|
+
- **IPv4-mapped IPv6:** Automatically handled (e.g., `::ffff:127.0.0.1` matches `127.0.0.1`)
|
|
43
|
+
|
|
44
|
+
## Auth-User Header
|
|
45
|
+
|
|
46
|
+
When requests come from a trusted proxy, the plugin can extract an authenticated username from a header set by the proxy. This is useful for:
|
|
47
|
+
|
|
48
|
+
- Logging who made the request (even if LDAP-Rest doesn't handle authentication)
|
|
49
|
+
- Passing identity from an upstream authentication system
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Custom header name (default: Auth-User)
|
|
53
|
+
DM_TRUSTED_PROXY_AUTH_HEADER="X-Remote-User"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The extracted username is:
|
|
57
|
+
|
|
58
|
+
- Available as `req.proxyAuthUser` for other plugins
|
|
59
|
+
- Used by the `weblogs` plugin to log the user field
|
|
60
|
+
|
|
61
|
+
## Example: nginx Configuration
|
|
62
|
+
|
|
63
|
+
```nginx
|
|
64
|
+
upstream ldap_rest {
|
|
65
|
+
server 127.0.0.1:8081;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
server {
|
|
69
|
+
listen 443 ssl;
|
|
70
|
+
server_name api.example.com;
|
|
71
|
+
|
|
72
|
+
location / {
|
|
73
|
+
proxy_pass http://ldap_rest;
|
|
74
|
+
proxy_set_header Host $host;
|
|
75
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
76
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
77
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
78
|
+
|
|
79
|
+
# Optional: Pass authenticated user from nginx auth
|
|
80
|
+
proxy_set_header Auth-User $remote_user;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
LDAP-Rest configuration:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
DM_TRUSTED_PROXIES="127.0.0.1"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Example: HAProxy Configuration
|
|
92
|
+
|
|
93
|
+
```haproxy
|
|
94
|
+
frontend https
|
|
95
|
+
bind *:443 ssl crt /etc/ssl/cert.pem
|
|
96
|
+
default_backend ldap_rest
|
|
97
|
+
|
|
98
|
+
backend ldap_rest
|
|
99
|
+
option forwardfor
|
|
100
|
+
http-request set-header X-Forwarded-Proto https
|
|
101
|
+
server ldap_rest 127.0.0.1:8081
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Use Cases
|
|
105
|
+
|
|
106
|
+
- **Reverse Proxy Deployments:** Ensure correct client IP for rate limiting and logging
|
|
107
|
+
- **Load Balancers:** Trust headers from known load balancer IPs
|
|
108
|
+
- **CDN Integration:** Trust headers from CDN edge servers
|
|
109
|
+
- **Kubernetes:** Trust headers from ingress controllers
|
|
110
|
+
- **Security:** Prevent attackers from spoofing `X-Forwarded-For` to bypass rate limits
|
|
111
|
+
|
|
112
|
+
## Security Considerations
|
|
113
|
+
|
|
114
|
+
- **Only trust known proxies:** Never use `0.0.0.0/0` or trust all IPs
|
|
115
|
+
- **Use specific IPs/ranges:** Limit to your actual proxy infrastructure
|
|
116
|
+
- **HTTPS between proxy and LDAP-Rest:** Prevent header injection attacks
|
|
117
|
+
- **Monitor logs:** Watch for warnings about removed X-Forwarded-For headers
|
|
118
|
+
|
|
119
|
+
## Plugin Load Order
|
|
120
|
+
|
|
121
|
+
The `trustedProxy` plugin is automatically loaded **first** (via `priority.json`) to ensure all other plugins see sanitized headers:
|
|
122
|
+
|
|
123
|
+
1. `core/auth/trustedProxy` - Sanitizes X-Forwarded-For headers
|
|
124
|
+
2. `core/weblogs` - Logs requests with correct client IP
|
|
125
|
+
3. `core/auth/crowdsec` - Checks IP reputation
|
|
126
|
+
4. `core/auth/rateLimit` - Rate limits by IP
|
|
127
|
+
5. Authentication plugins...
|