#23733 closed defect (invalid)
JOSM does not send client_secret in oauth2 token request for authorization code grant flow
| Reported by: | Woazboat | Owned by: | Woazboat |
|---|---|---|---|
| Priority: | critical | Milestone: | |
| Component: | Core | Version: | latest |
| Keywords: | oauth2, client_secret | Cc: |
Description (last modified by )
Encountered on JOSM v19103 when using a local self-hosted OSM server
After pressing the 'Authorize now (Fully automatic)' button, the following HTTP exchange happens:
Authorization request -> OSM server
GET /oauth2/authorize?response_type=code&client_id=Z6bOm_8NT2JIEx5JDpV4iQwy1vD40Pc6HYAu4ugUWgc&redirect_uri=http://127.0.0.1:8111/oauth_authorization&scope=read_gpx%20write_gpx%20read_prefs%20write_prefs%20write_api%20write_notes&state=9c24c6cd-5572-410f-bab3-fa49c91a71cd&code_challenge_method=S256&code_challenge=JjuiikdPYf6hiXWwzZW5ydJur-lqn7AdC_mH0WBORDA HTTP/1.1 Host: localhost:31500 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br DNT: 1 Connection: keep-alive Cookie: _osm_session=f1dd39db183fe58b9806ee39742ecc9f Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1
OSM server sends the authorization code back to JOSM via the remote control url:
HTTP/1.1 302 Found X-Frame-Options: SAMEORIGIN X-XSS-Protection: 0 X-Content-Type-Options: nosniff X-Permitted-Cross-Domain-Policies: none Referrer-Policy: strict-origin-when-cross-origin Content-Language: en Location: http://127.0.0.1:8111/oauth_authorization?code=1Zhog9xRXeAsbRwVUgVvtubQkmWeBuY_kr7SY_pkVy8&state=9c24c6cd-5572-410f-bab3-fa49c91a71cd Content-Type: text/html; charset=utf-8 Cache-Control: no-cache Content-Security-Policy-Report-Only: default-src 'self'; child-src 'self'; connect-src 'self'; font-src 'none'; frame-ancestors 'self'; frame-src 'self'; img-src 'self' data: www.gravatar.com *.wp.com tile.openstreetmap.org gps.tile.openstreetmap.org *.tile.thunderforest.com tile.tracestrack.com *.openstreetmap.fr; manifest-src 'self'; media-src 'none'; object-src 'self'; script-src 'self'; style-src 'self' 'nonce-rbBl3U4wpaHEQJMZrruF+qw9tDDD+sXC'; worker-src 'none' Set-Cookie: _osm_session=f1dd39db183fe58b9806ee39742ecc9f; path=/; expires=Thu, 11 Jul 2024 13:17:31 GMT; HttpOnly; SameSite=Lax X-Request-Id: cc4fcc48-4d44-4d1c-80b2-600ff2ee7d2f X-Runtime: 0.120389 Server-Timing: start_processing.action_controller;dur=0.09, cache_read.active_support;dur=0.14, sql.active_record;dur=49.07, instantiation.active_record;dur=2.14, transaction.active_record;dur=16.65, redirect_to.action_controller;dur=0.14, process_action.action_controller;dur=87.95, cache_write.active_support;dur=0.10 vary: Accept-Language, Origin Content-Length: 0 Date: Thu, 13 Jun 2024 13:17:31 GMT Server: lighttpd/1.4.64
GET /oauth_authorization?code=1Zhog9xRXeAsbRwVUgVvtubQkmWeBuY_kr7SY_pkVy8&state=9c24c6cd-5572-410f-bab3-fa49c91a71cd HTTP/1.1 Host: 127.0.0.1:8111 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br DNT: 1 Connection: keep-alive Cookie: _xsrf=2|1ee5c13c|6a4925189a1640610ef6a475ff957ba5|1617984267; CSRF-Token-3KZHS=uydXZvUwRpA3Jud2FgNt4i3QUmFp7PUu; session=eyJjc3JmX3Rva2VuIjoiNWJlYjY1N2I0YTk0OTQwY2Y1M2RlN2EyYmNkMGEyODYxNzkzOTQ5MSJ9.Yq0O7g.ckj-pvRrRe21b4q7kb41nhU9tZQ Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1 Sec-GPC: 1
In response, JOSM sends a POST request to the token access URL to fetch the token. The body of this request is missing the required client_secret field.
POST /oauth2/token HTTP/1.1 User-Agent: JOSM/1.5 (19103 en) Linux Debian GNU/Linux trixie/sid Java/17.0.11 Accept: */* Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Host: localhost:31500 Connection: keep-alive Content-Length: 238 Cookie: _osm_session=04f15c9d111f742de724847cd34caa4b grant_type=authorization_code&client_id=Z6bOm_8NT2JIEx5JDpV4iQwy1vD40Pc6HYAu4ugUWgc&redirect_uri=http://127.0.0.1:8111/oauth_authorization&code=1Zhog9xRXeAsbRwVUgVvtubQkmWeBuY_kr7SY_pkVy8&code_verifier=f7cfbb5d-87a2-4169-9cf3-62ed0020a9e4
As a result, the server rejects the token request:
HTTP/1.1 401 Unauthorized
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0
X-Content-Type-Options: nosniff
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
Cache-Control: no-store
Content-Type: application/json; charset=utf-8
WWW-Authenticate: Bearer realm="Doorkeeper", error="invalid_client", error_description="Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method."
Content-Security-Policy-Report-Only: default-src 'self'; child-src 'self'; connect-src 'self'; font-src 'none'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self' data: www.gravatar.com *.wp.com tile.openstreetmap.org gps.tile.openstreetmap.org *.tile.thunderforest.com tile.tracestrack.com *.openstreetmap.fr; manifest-src 'self'; media-src 'none'; object-src 'self'; script-src 'self'; style-src 'self' 'nonce-XzjPNRrxCTsi4OWcceuk1Z/uJARIa2kH'; worker-src 'none'
X-Request-Id: 1e9532ba-6621-4a14-8eb3-078c28565325
X-Runtime: 0.032401
Server-Timing: start_processing.action_controller;dur=0.02, sql.active_record;dur=1.51, instantiation.active_record;dur=0.29, process_action.action_controller;dur=12.66
vary: Accept, Origin
Content-Length: 173
Date: Thu, 13 Jun 2024 13:17:31 GMT
Server: lighttpd/1.4.64
{"error":"invalid_client","error_description":"Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method."}
After manually modifying and re-sending the POST request to include the client_secret field, the request succeeds and the server responds with a token:
(The following request logs for the modified request are from a different attempt, so the authorization codes/nonces are different)
POST /oauth2/token HTTP/1.1 User-Agent: JOSM/1.5 (19103 en) Linux Debian GNU/Linux trixie/sid Java/17.0.11 Accept: */* Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Host: localhost:31500 Connection: keep-alive Content-Length: 296 Cookie: _osm_session=04f15c9d111f742de724847cd34caa4b grant_type=authorization_code&client_id=Z6bOm_8NT2JIEx5JDpV4iQwy1vD40Pc6HYAu4ugUWgc&client_secret=2TvpyFlxvu0C4b435d3lofB5dY7K4_qSKWoX6C0LEWM&redirect_uri=http://127.0.0.1:8111/oauth_authorization&code=IO4jNs0TqwV4o0Qxc0Itr1k-EzkttoI4s_EiAA5VxeA&code_verifier=bb1210fd-b70f-4d5b-b260-2264a2249f47
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0
X-Content-Type-Options: nosniff
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
Cache-Control: no-store
Content-Type: application/json; charset=utf-8
ETag: W/"3ac628dafb309e963cfeaf02f5dab579"
Content-Security-Policy-Report-Only: default-src 'self'; child-src 'self'; connect-src 'self'; font-src 'none'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self' data: www.gravatar.com *.wp.com tile.openstreetmap.org gps.tile.openstreetmap.org *.tile.thunderforest.com tile.tracestrack.com *.openstreetmap.fr; manifest-src 'self'; media-src 'none'; object-src 'self'; script-src 'self'; style-src 'self' 'nonce-i9hz2NMTdzxzWhLUI8Z4eCIksw+JEXzN'; worker-src 'none'
X-Request-Id: b7b5e3e1-84a5-4f58-a090-269eed9e7620
X-Runtime: 0.051822
Server-Timing: start_processing.action_controller;dur=0.02, sql.active_record;dur=13.24, instantiation.active_record;dur=0.73, transaction.active_record;dur=18.50, process_action.action_controller;dur=33.36
vary: Accept, Origin
Content-Length: 182
Date: Thu, 13 Jun 2024 13:30:25 GMT
Server: lighttpd/1.4.64
{"access_token":"oxpk2rfgDuAlRMP0T25b5v3LNXJF99PK1451H8Rm1wM","token_type":"Bearer","scope":"read_gpx write_gpx read_prefs write_prefs write_api write_notes","created_at":1718284075}
The server is only used for local testing, so posting the secrets here is not an issue
References:
https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
https://developer.okta.com/blog/2018/04/10/oauth-authorization-code-grant-type
https://www.oauth.com/playground/authorization-code.html
Attachments (1)
Change History (7)
by , 19 months ago
| Attachment: | josm_custom_server_oauth2_settings.png added |
|---|
comment:1 by , 19 months ago
| Description: | modified (diff) |
|---|
comment:2 by , 19 months ago
| Description: | modified (diff) |
|---|
follow-up: 5 comment:3 by , 19 months ago
RFC6749 section 2.3.1 ("Client Password") isn't currently supported by JOSM.
For starters, JOSM is not a "confidential" application -- since we distribute a jar file with the parameters, the client_secret is by definition not secret. From section 2.1, when generating tokens for JOSM on osm.org, I chose the "public" (non-confidential) option. I only added the secret for the dev instance to make it easier for contributors to test functionality that required it.
We use the Authorization Code Grant flow (section 4.1).
Specifically:
4.1.1: No client secret
4.1.2: No client secret
4.1.3: No client secret
OK. Where does the confidential application actually use the client secret:
2.3.1: Basic auth with authorization server. From what I understand, this should be disabled on osm.org anyway.
For this flow, I would have a user ("user") and password ("password123") which would be Authorization: Basic dXNlcjpwYXNzd29yZDEyMw== with a POST like /token and a body of grant_type=token&client_id=${client_id}&client_secret=${client_secret}.
All that we can do with the client_secret is avoid opening a browser; this is generally considered bad practice for 3p clients anyway.
In any case, I specifically wrote our OAuth 2 implementation with a view towards "best practices".
comment:4 by , 19 months ago
| Owner: | changed from to |
|---|---|
| Status: | new → needinfo |
I think your client_id might be wrong. Can you show the oauth2 settings from your local OSM server?
comment:5 by , 19 months ago
Ah, yes you are correct, this was a configuration error on my side and a misunderstanding about oauth. I left the 'confidential application' checkbox on the default 'on' state when registering the oauth application on the server. The token access request works after deselecting that setting.
Replying to taylor.smock:
OK. Where does the confidential application actually use the client secret:
2.3.1: Basic auth with authorization server. From what I understand, this should be disabled on osm.org anyway.
For this flow, I would have a user ("user") and password ("password123") which would beAuthorization: Basic dXNlcjpwYXNzd29yZDEyMw==with a POST like/tokenand a body ofgrant_type=token&client_id=${client_id}&client_secret=${client_secret}.
As far as I understand it, only one of either basic auth or the client secret in the body should be used for client authentication. At least with the default configuration, the rails port accepts the client secret as authentication (as seen above). Not sure if the configuration for osm.org differs.
I think your client_id might be wrong. Can you show the oauth2 settings from your local OSM server?
The OSM website source doesn't include the default client id config for josm, so the one in the requests above is a custom one for a self-registered oauth application.
comment:6 by , 19 months ago
| Resolution: | → invalid |
|---|---|
| Status: | needinfo → closed |




oauth2 settings