-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Describe the bug
Some clients may try to sent the client id for device code flow via Authentication header instead of the POST body:
POST /application/o/device/ HTTP/1.1
Host: authentik.company
Authorization: Basic <encoded>
where <encoded> is a placeholder for the base64 encoded string of client_id: with no password.
When trying to use device code flow with such a client instead of the expected response described here I am getting a "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)" error.
The below example sending the client id in the POST body works, but the subsequent example does not:
$ curl -v -X POST http://localhost:9000/application/o/device/ -d "client_id=xmhYexEdD3F1idgo9jtZj0Ys4QUbmbjeYFlxkZ8I"
Note: Unnecessary use of -X or --request, POST is already inferred.
* Host localhost:9000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:9000...
* Connected to localhost (::1) port 9000
> POST /application/o/device/ HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/8.7.1
> Accept: */*
> Content-Length: 50
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 50 bytes
< HTTP/1.1 200 OK
...
* Connection #0 to host localhost left intact
{"device_code": "NV/Nri\\}/Xau-|Kd<RSPT?R8KqLM5;_;MeY[#,MmO!AOg)T:!]9\"~GBJW~Gy5=d4'>H,]*$)Y:B,+s-Df(F9.q~b*j8.C+@*@:vc5k~&fmZaLn]Br\":&9hO`YLa.7R_M", "verification_uri": "http://localhost:9000/device", "verification_uri_complete": "http://localhost:9000/device?code=889650075", "user_code": "889650075", "expires_in": 60, "interval": 5How to reproduce
- Create and apply device code flow, https://docs.goauthentik.io/add-secure-apps/providers/oauth2/device_code/#create-and-apply-a-device-code-flow
- Create an OAuth2 provider
- Copy client id
- Run
curl -v -X POST https://<domain-for-authentik>/application/o/device/ -u "<client-id>:" - Get response
{"error": "invalid_client", "error_description": "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)", "request_id": "<request-id>"}
Example:
$ curl -v -X POST http://localhost:9000/application/o/device/ -u "xmhYexEdD3F1idgo9jtZj0Ys4QUbmbjeYFlxkZ8I:"
* Host localhost:9000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:9000...
* Connected to localhost (::1) port 9000
* Server auth using Basic with user 'xmhYexEdD3F1idgo9jtZj0Ys4QUbmbjeYFlxkZ8I'
> POST /application/o/device/ HTTP/1.1
> Host: localhost:9000
> Authorization: Basic eG1oWWV4RWREM0YxaWRnbzlqdFpqMFlzNFFVYm1iamVZRmx4a1o4STo=
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 400 Bad Request
...
* Connection #0 to host localhost left intact
{"error": "invalid_client", "error_description": "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)", "request_id": "bcd3973fcb3f4f5c9a17aa90c60d4211"}Expected behavior
Should get a successful response (HTTP 200) with body as described in https://docs.goauthentik.io/add-secure-apps/providers/oauth2/device_code/#device-flow-initiation.
Example:
$ curl -v -X POST http://localhost:9000/application/o/device/ -u "xmhYexEdD3F1idgo9jtZj0Ys4QUbmbjeYFlxkZ8I:"
* Host localhost:9000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:9000...
* Connected to localhost (::1) port 9000
* Server auth using Basic with user 'xmhYexEdD3F1idgo9jtZj0Ys4QUbmbjeYFlxkZ8I'
> POST /application/o/device/ HTTP/1.1
> Host: localhost:9000
> Authorization: Basic eG1oWWV4RWREM0YxaWRnbzlqdFpqMFlzNFFVYm1iamVZRmx4a1o4STo=
> User-Agent: curl/8.7.1
> Accept: */*
>
< HTTP/1.1 200 OK
...
* Connection #0 to host localhost left intact
{"device_code": "NV/Nri\\}/Xau-|Kd<RSPT?R8KqLM5;_;MeY[#,MmO!AOg)T:!]9\"~GBJW~Gy5=d4'>H,]*$)Y:B,+s-Df(F9.q~b*j8.C+@*@:vc5k~&fmZaLn]Br\":&9hO`YLa.7R_M", "verification_uri": "http://localhost:9000/device", "verification_uri_complete": "http://localhost:9000/device?code=889650075", "user_code": "889650075", "expires_in": 60, "interval": 5Screenshots
No response
Additional context
Per RFC 8628
The client authentication requirements of Section 3.2.1 of [RFC6749]
apply to requests on this endpoint, which means that confidential
clients (those that have established client credentials) authenticate
in the same manner as when making requests to the token endpoint, and
public clients provide the "client_id" parameter to identify
themselves.
and Authentik already supports HTTP Basic Authentication elsewhere:
authentik/authentik/providers/oauth2/utils.py
Lines 110 to 131 in 1031b05
| def extract_client_auth(request: HttpRequest) -> tuple[str, str]: | |
| """ | |
| Get client credentials using HTTP Basic Authentication method. | |
| Or try getting parameters via POST. | |
| See: http://tools.ietf.org/html/rfc6750#section-2.1 | |
| Return a tuple `(client_id, client_secret)`. | |
| """ | |
| auth_header = request.META.get("HTTP_AUTHORIZATION", "") | |
| if re.compile(r"^Basic\s{1}.+$").match(auth_header): | |
| b64_user_pass = auth_header.split()[1] | |
| try: | |
| user_pass = b64decode(b64_user_pass).decode("utf-8").partition(":") | |
| client_id, _, client_secret = user_pass | |
| except ValueError, Error: | |
| client_id = client_secret = "" # nosec | |
| else: | |
| client_id = request.POST.get("client_id", "") | |
| client_secret = request.POST.get("client_secret", "") | |
| return (client_id, client_secret) |
Deployment Method
Docker
Version
2025.12.4
Relevant log output
From docker logs when making request
server-1 | {"auth_via": "unauthenticated", "domain_url": "localhost", "event": "/application/o/device/", "host": "localhost:9000", "level": "info", "logger": "authentik.asgi", "method": "POST", "pid": 169, "remote": "192.168.148.1", "request_id": "9db5ee0eeed74c60bb9b97779cbab9b7", "runtime": 16, "schema_name": "public", "scheme": "http", "status": 400, "timestamp": "2026-02-21T17:54:04.908386", "user": "", "user_agent": "curl/8.7.1"}