Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/api-guide/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ When an unauthenticated request is denied permission there are two different err
* [HTTP 401 Unauthorized][http401]
* [HTTP 403 Permission Denied][http403]

HTTP 401 responses must always include a `WWW-Authenticate` header, that instructs the client how to authenticate. HTTP 403 responses do not include the `WWW-Authenticate` header.
HTTP 401 responses must always include a `WWW-Authenticate` header, that instructs the client how to authenticate. The `www_authenticate_behavior` setting controls how the header is generated: if set to `'first'` (the default), then only the text for the first scheme in the list will be used; if set to `'all'`, then a comma-separated list of the text for all the schemes will be used (see [MDN WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate) for more details). HTTP 403 responses do not include the `WWW-Authenticate` header.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting name should be uppercase

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph refers to a www_authenticate_behavior setting, but the documented/global setting name added in this PR is WWW_AUTHENTICATE_BEHAVIOR (and the per-view attribute is www_authenticate_behavior). Please rename here to avoid pointing users to a non-existent REST_FRAMEWORK key.

Suggested change
HTTP 401 responses must always include a `WWW-Authenticate` header, that instructs the client how to authenticate. The `www_authenticate_behavior` setting controls how the header is generated: if set to `'first'` (the default), then only the text for the first scheme in the list will be used; if set to `'all'`, then a comma-separated list of the text for all the schemes will be used (see [MDN WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate) for more details). HTTP 403 responses do not include the `WWW-Authenticate` header.
HTTP 401 responses must always include a `WWW-Authenticate` header, that instructs the client how to authenticate. The `WWW_AUTHENTICATE_BEHAVIOR` setting controls how the header is generated: if set to `'first'` (the default), then only the text for the first scheme in the list will be used; if set to `'all'`, then a comma-separated list of the text for all the schemes will be used (see [MDN WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate) for more details). HTTP 403 responses do not include the `WWW-Authenticate` header.

Copilot uses AI. Check for mistakes.

The kind of response that will be used depends on the authentication scheme. Although multiple authentication schemes may be in use, only one scheme may be used to determine the type of response. **The first authentication class set on the view is used when determining the type of response**.
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With WWW_AUTHENTICATE_BEHAVIOR='all', the 401 vs 403 decision is no longer determined solely by the first authentication class (any authenticator that returns a challenge will keep the response as 401). Please update/qualify this sentence so it remains accurate under the new setting.

Copilot uses AI. Check for mistakes.

Expand Down
7 changes: 7 additions & 0 deletions docs/api-guide/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ The class that should be used to initialize `request.auth` for unauthenticated r

Default: `None`

#### WWW_AUTHENTICATE_BEHAVIOR

Determines whether a single or multiple challenges are presented in the `WWW-Authenticate` header.

This should be set to `'first'` (the default value) or `'all'`. When set to `'first'`, the `WWW-Authenticate` header will be set to an appropriate challenge for the first authentication scheme in the list.
When set to `'all'`, a comma-separated list of the challenge for all specified authentication schemes will be used instead (following the [syntax specification](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate)).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RFC 9110 also warns:

Some user agents do not recognize this form, however. As a result, sending a WWW-Authenticate field value with more than one member on the same field line might not be interoperable.

Perhaps we should have a similar warning somewhere, either here or in authentication.md.

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar: “a comma-separated list of the challenge for all specified authentication schemes” should be “...list of the challenges...” (plural).

Suggested change
When set to `'all'`, a comma-separated list of the challenge for all specified authentication schemes will be used instead (following the [syntax specification](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate)).
When set to `'all'`, a comma-separated list of the challenges for all specified authentication schemes will be used instead (following the [syntax specification](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate)).

Copilot uses AI. Check for mistakes.

---

## Test settings
Expand Down
1 change: 1 addition & 0 deletions rest_framework/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
# Authentication
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_TOKEN': None,
'WWW_AUTHENTICATE_BEHAVIOR': 'first',
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this a boolean, e.g. WWW_AUTHENTICATE_ALL = False (by default)?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose I was leaving the door open for other modes beyond 'first' and 'all', but I think a boolean setting makes more sense here.


# View configuration
'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
Expand Down
8 changes: 7 additions & 1 deletion rest_framework/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class APIView(View):
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
www_authenticate_behavior = api_settings.WWW_AUTHENTICATE_BEHAVIOR
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
Expand Down Expand Up @@ -186,8 +187,13 @@ def get_authenticate_header(self, request):
header to use for 401 responses, if any.
"""
authenticators = self.get_authenticators()
www_authenticate_behavior = self.www_authenticate_behavior
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_authenticate_header() silently returns None for any www_authenticate_behavior value other than 'first'/'all', which will coerce NotAuthenticated into a 403. Since www_authenticate_behavior can be overridden per-view (bypassing the system check), consider adding an explicit fallback (eg default to 'first') or raising a clear configuration error so misconfiguration doesn’t change response semantics silently.

Suggested change
www_authenticate_behavior = self.www_authenticate_behavior
www_authenticate_behavior = self.www_authenticate_behavior
# Ensure that misconfiguration of `www_authenticate_behavior` does not
# silently change response semantics. Fall back to the default
# behavior of using the first authenticator if an unexpected value
# is provided at the view level.
if www_authenticate_behavior not in ('first', 'all'):
www_authenticate_behavior = 'first'

Copilot uses AI. Check for mistakes.
Comment on lines 192 to +196
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new 'all' behavior is untested. There are existing tests asserting the WWW-Authenticate header for 401s; please add coverage for multiple authentication classes with WWW_AUTHENTICATE_BEHAVIOR='all' (and 'first') to ensure the header value and status-code behavior stay stable.

Copilot uses AI. Check for mistakes.
if authenticators:
return authenticators[0].authenticate_header(request)
if www_authenticate_behavior == 'first':
return authenticators[0].authenticate_header(request)
elif www_authenticate_behavior == 'all':
challenges = (a.authenticate_header(request) for a in authenticators)
return ', '.join((c for c in challenges if c is not None))
Comment thread
waxlamp marked this conversation as resolved.

def get_parser_context(self, http_request):
"""
Expand Down