Skip to content

Commit e89e62a

Browse files
committed
Add OAuth 2.0/2.1 documentation page
1 parent defff9c commit e89e62a

3 files changed

Lines changed: 363 additions & 364 deletions

File tree

_includes/docs-sidebar.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ <h3 class="mt-2">Security</h3>
110110
<li><a href="/docs/security/user-management">User Management</a></li>
111111
<li><a href="/docs/security/permissions">Permissions</a></li>
112112
<li><a href="/docs/security/how-clients-authenticate">Client Authentication</a></li>
113+
<li><a href="/docs/security/oauth">OAuth 2.0 / 2.1</a></li>
113114
<li><a href="/docs/security/tls">TLS Configuration</a></li>
114115
<li><a href="/docs/security/security-hardening">Security Hardening</a></li>
115116
<li><a href="/docs/security/secure-connection-to-mongodb">Secure MongoDB Connection</a></li>

docs/security/how-clients-authenticate.adoc

Lines changed: 9 additions & 364 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ In this guide, you'll learn how to:
1919

2020
. Authenticate using Basic Authentication with username and password
2121
. Understand how the Authorization header works
22-
. Use authentication tokens for subsequent requests (password and client_credentials grant types)
23-
. Auto-discover server metadata via the RFC 8414 discovery endpoint
24-
. Manage authentication tokens (retrieve and invalidate)
22+
. Obtain and use OAuth 2.0 access tokens
2523
. Check user credentials and roles
2624
. Avoid browser authentication popups
2725

@@ -105,371 +103,18 @@ In other words:
105103

106104
=== Authentication Token
107105

108-
==== Modern OAuth 2.0 Token Endpoint (Recommended)
106+
RESTHeart v9 provides a standards-compliant OAuth 2.0/2.1 token endpoint.
107+
See the link:/docs/security/oauth[OAuth 2.0 / 2.1] page for full documentation on:
109108

110-
RESTHeart v9 introduces dedicated OAuth 2.0-compatible token endpoints for secure and standards-compliant authentication. This is the recommended approach for new applications.
111-
112-
**Available Endpoints:**
113-
114-
* `POST /token` - Returns JWT token in response body (supports `password` and `client_credentials` grant types)
115-
* `POST /token/cookie` - Sets JWT token as HttpOnly cookie (enhanced security for browser-based apps)
116-
* `GET /.well-known/oauth-authorization-server` - Returns server metadata for client auto-discovery (RFC 8414)
117-
118-
**Benefits:**
119-
120-
* 85% performance improvement over the legacy token injection approach
121-
* Standards-compliant OAuth 2.0 implementation
122-
* Reduced attack surface with dedicated endpoints
123-
* Centralized authentication audit trails
124-
* No overhead on every authenticated request
125-
126-
===== Basic Authentication Method
127-
128-
Send credentials using HTTP Basic Authentication:
129-
130-
==== cURL
131-
[source,bash]
132-
----
133-
curl -i -X POST [RESTHEART-URL]/token \
134-
-u [BASIC-AUTH]
135-
----
136-
137-
==== HTTPie
138-
[source,bash]
139-
----
140-
http POST [RESTHEART-URL]/token \
141-
Authorization:"Basic [BASIC-AUTH]"
142-
----
143-
144-
==== JavaScript
145-
[source,javascript]
146-
----
147-
const username = 'your-username';
148-
const password = 'your-password';
149-
const credentials = btoa(`${username}:${password}`);
150-
151-
fetch('[RESTHEART-URL]/token', {
152-
method: 'POST',
153-
headers: {
154-
'Authorization': `Basic ${credentials}`
155-
}
156-
})
157-
.then(response => response.json())
158-
.then(data => {
159-
console.log('Access token:', data.access_token);
160-
console.log('Expires in:', data.expires_in, 'seconds');
161-
// Store the token for future requests
162-
sessionStorage.setItem('auth_token', data.access_token);
163-
})
164-
.catch(error => console.error('Error:', error));
165-
----
166-
167-
**Response:**
168-
169-
[source,json]
170-
----
171-
{
172-
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
173-
"token_type": "Bearer",
174-
"expires_in": 900
175-
}
176-
----
177-
178-
===== OAuth 2.0 Form Data Method (Resource Owner Password Credentials)
179-
180-
Send credentials using OAuth 2.0 form data (application/x-www-form-urlencoded) with `grant_type=password` (RFC 6749 §4.3):
181-
182-
==== cURL
183-
[source,bash]
184-
----
185-
curl -i -X POST [RESTHEART-URL]/token \
186-
-H "Content-Type: application/x-www-form-urlencoded" \
187-
-d "grant_type=password&username=admin&password=secret"
188-
----
189-
190-
==== HTTPie
191-
[source,bash]
192-
----
193-
http -f POST [RESTHEART-URL]/token \
194-
grant_type=password \
195-
username=admin \
196-
password=secret
197-
----
198-
199-
==== JavaScript
200-
[source,javascript]
201-
----
202-
const formData = new URLSearchParams({
203-
grant_type: 'password',
204-
username: 'your-username',
205-
password: 'your-password'
206-
});
207-
208-
fetch('[RESTHEART-URL]/token', {
209-
method: 'POST',
210-
headers: {
211-
'Content-Type': 'application/x-www-form-urlencoded'
212-
},
213-
body: formData
214-
})
215-
.then(response => response.json())
216-
.then(data => {
217-
console.log('Access token:', data.access_token);
218-
sessionStorage.setItem('auth_token', data.access_token);
219-
})
220-
.catch(error => console.error('Error:', error));
221-
----
222-
223-
===== Client Credentials Grant
224-
225-
NOTE: `client_credentials` grant type support is available from RESTHeart v9.2.0.
226-
227-
For machine-to-machine or service-account authentication, use `grant_type=client_credentials` (RFC 6749 §4.4).
228-
Pass `client_id` and `client_secret` in the request body instead of `username` and `password`.
229-
This is the recommended approach for API gateways, CLI tools, and MCP clients.
230-
231-
==== cURL
232-
[source,bash]
233-
----
234-
curl -i -X POST [RESTHEART-URL]/token \
235-
-H "Content-Type: application/x-www-form-urlencoded" \
236-
-d "grant_type=client_credentials&client_id=myapp&client_secret=s3cret"
237-
----
238-
239-
==== HTTPie
240-
[source,bash]
241-
----
242-
http -f POST [RESTHEART-URL]/token \
243-
grant_type=client_credentials \
244-
client_id=myapp \
245-
client_secret=s3cret
246-
----
247-
248-
==== JavaScript
249-
[source,javascript]
250-
----
251-
const formData = new URLSearchParams({
252-
grant_type: 'client_credentials',
253-
client_id: 'your-client-id',
254-
client_secret: 'your-client-secret'
255-
});
256-
257-
fetch('[RESTHEART-URL]/token', {
258-
method: 'POST',
259-
headers: {
260-
'Content-Type': 'application/x-www-form-urlencoded'
261-
},
262-
body: formData
263-
})
264-
.then(response => response.json())
265-
.then(data => {
266-
console.log('Access token:', data.access_token);
267-
console.log('Expires in:', data.expires_in, 'seconds');
268-
})
269-
.catch(error => console.error('Error:', error));
270-
----
271-
272-
The response format is identical to the password grant:
273-
274-
[source,json]
275-
----
276-
{
277-
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
278-
"token_type": "Bearer",
279-
"expires_in": 900,
280-
"username": "myapp",
281-
"roles": ["service"]
282-
}
283-
----
284-
285-
NOTE: `client_id` and `client_secret` are mapped to the user credentials in the configured authenticator (e.g., `mongoRealmAuthenticator`). The client account must exist in the user store just like a regular user account.
286-
287-
===== Using the Token
288-
289-
Once you have the token, use it as a Bearer token in the Authorization header:
290-
291-
==== cURL
292-
[source,bash]
293-
----
294-
curl -i -X GET [RESTHEART-URL]/mycollection \
295-
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
296-
----
297-
298-
==== HTTPie
299-
[source,bash]
300-
----
301-
http GET [RESTHEART-URL]/mycollection \
302-
"Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
303-
----
304-
305-
==== JavaScript
306-
[source,javascript]
307-
----
308-
const token = sessionStorage.getItem('auth_token');
309-
310-
fetch('[RESTHEART-URL]/mycollection', {
311-
method: 'GET',
312-
headers: {
313-
'Authorization': `Bearer ${token}`
314-
}
315-
})
316-
.then(response => response.json())
317-
.then(data => console.log('Data:', data))
318-
.catch(error => console.error('Error:', error));
319-
----
320-
321-
===== Token Renewal
322-
323-
To renew an authentication token before it expires, add the `?renew` query parameter to `GET /token` or `GET /token/cookie` requests:
324-
325-
==== cURL
326-
[source,bash]
327-
----
328-
curl -i -X GET [RESTHEART-URL]/token?renew \
329-
-u [BASIC-AUTH]
330-
----
331-
332-
==== HTTPie
333-
[source,bash]
334-
----
335-
http GET [RESTHEART-URL]/token?renew \
336-
Authorization:"Basic [BASIC-AUTH]"
337-
----
338-
339-
==== JavaScript
340-
[source,javascript]
341-
----
342-
const username = 'your-username';
343-
const password = 'your-password';
344-
const credentials = btoa(`${username}:${password}`);
345-
346-
fetch('[RESTHEART-URL]/token?renew', {
347-
method: 'GET',
348-
headers: {
349-
'Authorization': `Basic ${credentials}`
350-
}
351-
})
352-
.then(response => response.json())
353-
.then(data => {
354-
console.log('New access token:', data.access_token);
355-
sessionStorage.setItem('auth_token', data.access_token);
356-
})
357-
.catch(error => console.error('Error:', error));
358-
----
359-
360-
NOTE: Generating a new token is a cryptographic operation and can have significant performance overhead. It is the responsibility of the client to renew the token using this query parameter when it is going to expire soon.
361-
362-
===== Cookie-Based Authentication
363-
364-
For browser-based applications, use the `/token/cookie` endpoint to set an HttpOnly cookie (more secure as the token isn't exposed to JavaScript):
365-
366-
==== cURL
367-
[source,bash]
368-
----
369-
curl -i -X POST [RESTHEART-URL]/token/cookie \
370-
-u [BASIC-AUTH] \
371-
-c cookies.txt
372-
----
373-
374-
==== HTTPie
375-
[source,bash]
376-
----
377-
http --session=./session.json POST [RESTHEART-URL]/token/cookie \
378-
Authorization:"Basic [BASIC-AUTH]"
379-
----
380-
381-
==== JavaScript
382-
[source,javascript]
383-
----
384-
const username = 'your-username';
385-
const password = 'your-password';
386-
const credentials = btoa(`${username}:${password}`);
387-
388-
fetch('[RESTHEART-URL]/token/cookie', {
389-
method: 'POST',
390-
headers: {
391-
'Authorization': `Basic ${credentials}`
392-
},
393-
credentials: 'include' // Important: include cookies
394-
})
395-
.then(response => {
396-
if (response.ok) {
397-
console.log('Authenticated! Cookie set.');
398-
// Subsequent requests will automatically include the cookie
399-
}
400-
})
401-
.catch(error => console.error('Error:', error));
402-
403-
// Subsequent requests automatically include the cookie
404-
fetch('[RESTHEART-URL]/mycollection', {
405-
method: 'GET',
406-
credentials: 'include' // Important: include cookies
407-
})
408-
.then(response => response.json())
409-
.then(data => console.log('Data:', data))
410-
.catch(error => console.error('Error:', error));
411-
----
412-
413-
**Configuration:**
414-
415-
The JWT Token Manager is enabled by default in RESTHeart v9:
416-
417-
[source,yml]
418-
----
419-
jwtTokenManager:
420-
key: secret # Change this in production!
421-
enabled: true
422-
ttl: 15 # Token time-to-live in minutes
423-
srv-uri: /tokens
424-
issuer: restheart.com
425-
----
426-
427-
IMPORTANT: Always change the `key` value in production to a strong, random secret.
428-
429-
===== OAuth 2.0 Authorization Server Metadata Discovery (RFC 8414)
430-
431-
NOTE: The authorization server metadata discovery endpoint is available from RESTHeart v9.2.0.
432-
433-
RESTHeart exposes a standards-compliant metadata discovery endpoint at `GET /.well-known/oauth-authorization-server` (RFC 8414).
434-
This allows OAuth 2.0 clients to auto-configure themselves without hardcoded URLs.
435-
436-
==== cURL
437-
[source,bash]
438-
----
439-
curl -i [RESTHEART-URL]/.well-known/oauth-authorization-server
440-
----
441-
442-
==== HTTPie
443-
[source,bash]
444-
----
445-
http GET [RESTHEART-URL]/.well-known/oauth-authorization-server
446-
----
447-
448-
**Response:**
449-
450-
[source,json]
451-
----
452-
{
453-
"issuer": "http://localhost:8080",
454-
"token_endpoint": "http://localhost:8080/token",
455-
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
456-
"grant_types_supported": ["password", "client_credentials"],
457-
"response_types_supported": ["token"]
458-
}
459-
----
460-
461-
The endpoint is publicly accessible (no authentication required) and the base URL is derived from the request `Host` header by default.
462-
You can override it with the `base-url` configuration option:
463-
464-
[source,yml]
465-
----
466-
oauthAuthorizationServerMetadataService:
467-
base-url: https://api.example.com # optional, falls back to request Host header
468-
----
109+
* `POST /token` — password, client_credentials, and authorization_code grants
110+
* `POST /token/cookie` — HttpOnly cookie for browser applications
111+
* Authorization Code + PKCE flow (OAuth 2.1, RFC 7636)
112+
* Authorization Server Metadata discovery (RFC 8414)
113+
* Protected Resource Metadata (RFC 9728)
469114

470115
==== Legacy Token Management (Automatic Injection)
471116

472-
NOTE: This is the legacy token management approach. For new applications, use the OAuth 2.0 `/token` endpoint described above.
117+
NOTE: This is the legacy token management approach. For new applications, use the link:/docs/security/oauth[OAuth 2.0 `/token` endpoint].
473118

474119
RESTHeart can also automatically inject auth tokens into response headers. The default configuration includes the **tokenBasicAuthMechanism** and the **rndTokenManager**.
475120

0 commit comments

Comments
 (0)