-
Notifications
You must be signed in to change notification settings - Fork 284
Closed as not planned
Description
Hello, following this conversation, I'm opening this issue for a potential bug in the OAuth flow.
Note
With help from thisismissem.social, I found a workable solution more in line with the intended flow design so all is good this is not an issue for me.
Context
- native Android client (Compose Multiplatform)
- https redirect uri
- custom html to extract code and state
Issue
The app is successful in getting an authorization code from the pds but unable to exchange it for credential tokens.
metadata JSON file
{
"client_id": "https://auth.ontempo.social/native/metadata/oauth-client-metadata.json",
"application_type": "native",
"response_types": ["code"],
"grant_types": ["authorization_code", "refresh_token"],
"dpop_bound_access_tokens": true,
"redirect_uris": ["https://auth.ontempo.social/native/callback/callback.html"],
"scope": "atproto transition:generic",
"token_endpoint_auth_method": "none"
}callback html file
<!-- place this file in ${DOCKER_CONFIG_DIR}/**/auth/native/callback and delete this line -->
<!DOCTYPE html>
<html>
<head>
<title>Tempo OAuth Callback</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Redirecting to app...</h1>
<p>If the app doesn't open automatically, please return to the app manually.</p>
<script>
// Extract all parameters from URL
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');
const iss = urlParams.get('iss');
const error = urlParams.get('error');
if (code && state) {
// Build redirect URL with all parameters
let redirectUrl = `co.ontempo.tempo://oauth/callback?code=${code}&state=${state}`;
if (iss) {
redirectUrl += `&iss=${encodeURIComponent(iss)}`;
}
window.location.href = redirectUrl;
} else if (error) {
window.location.href = `co.ontempo.tempo://oauth/callback?error=${error}`;
}
</script>
</body>
</html>
Application log
2025-12-09 22:14:07.117 26589-26589 AtProtoAuth co.ontempo.tempo I Authentication started for handle: nackko.ontempo.social
2025-12-09 22:14:07.143 26589-26672 HTTP co.ontempo.tempo D REQUEST: https://nackko.ontempo.social/.well-known/atproto-did
METHOD: GET
COMMON HEADERS
-> Accept: application/json
-> Accept-Charset: UTF-8
CONTENT HEADERS
-> Content-Length: 0
BODY Content-Type: null
BODY START
BODY END
2025-12-09 22:14:07.242 26589-26589 HTTP co.ontempo.tempo D RESPONSE: 200 OK
METHOD: GET
FROM: https://nackko.ontempo.social/.well-known/atproto-did
COMMON HEADERS
-> access-control-allow-origin: *
-> connection: keep-alive
-> content-length: 32
-> content-type: text/plain; charset=utf-8
-> date: Wed, 10 Dec 2025 03:14:05 GMT
-> etag: W/"20-4T29UDzW0hknFhntyFodS7z/46g"
-> server: openresty
-> strict-transport-security: max-age=63072000; preload
-> vary: Accept-Encoding
-> x-android-received-millis: 1765336447230
-> x-android-response-source: NETWORK 200
-> x-android-selected-protocol: http/1.1
-> x-android-sent-millis: 1765336447216
-> x-powered-by: Express
-> x-served-by: nackko.ontempo.social
BODY Content-Type: text/plain; charset=utf-8
BODY START
did:plc:syldnchxgxctqcjvkhqprrxz
BODY END
2025-12-09 22:14:07.245 26589-26589 AtProtoAuth co.ontempo.tempo D Resolved handle to DID: did:plc:syldnchxgxctqcjvkhqprrxz
2025-12-09 22:14:07.248 26589-26673 HTTP co.ontempo.tempo D REQUEST: https://plc.directory/did:plc:syldnchxgxctqcjvkhqprrxz
METHOD: GET
COMMON HEADERS
-> Accept: application/json
-> Accept-Charset: UTF-8
CONTENT HEADERS
-> Content-Length: 0
BODY Content-Type: null
BODY START
BODY END
2025-12-09 22:14:07.401 26589-26589 HTTP co.ontempo.tempo D RESPONSE: 200 OK
METHOD: GET
FROM: https://plc.directory/did:plc:syldnchxgxctqcjvkhqprrxz
COMMON HEADERS
-> access-control-allow-origin: *
-> connection: keep-alive
-> content-length: 551
-> content-type: application/did+ld+json; charset=utf-8
-> date: Wed, 10 Dec 2025 03:14:05 GMT
-> etag: W/"227-Syo10B6ojzWOYsxvvh75KHU1YPc"
-> x-android-received-millis: 1765336447393
-> x-android-response-source: NETWORK 200
-> x-android-selected-protocol: http/1.1
-> x-android-sent-millis: 1765336447329
-> x-powered-by: Express
BODY Content-Type: application/did+ld+json; charset=utf-8
BODY START
{"@context":["https://www.w3.org/ns/did/v1","https://w3id.org/security/multikey/v1","https://w3id.org/security/suites/secp256k1-2019/v1"],"id":"did:plc:syldnchxgxctqcjvkhqprrxz","alsoKnownAs":["at://nackko.ontempo.social"],"verificationMethod":[{"id":"did:plc:syldnchxgxctqcjvkhqprrxz#atproto","type":"Multikey","controller":"did:plc:syldnchxgxctqcjvkhqprrxz","publicKeyMultibase":"zQ3shfXLMEimkQEZL48y2ouKh9KXEnqYhdhtCJmjk5N1fNjqH"}],"service":[{"id":"#atproto_pds","type":"AtprotoPersonalDataServer","serviceEndpoint":"https://pds.ontempo.social"}]}
BODY END
2025-12-09 22:14:07.413 26589-26589 AtProtoAuth co.ontempo.tempo D Retrieved PDS URL: https://pds.ontempo.social
2025-12-09 22:14:07.415 26589-26673 HTTP co.ontempo.tempo D REQUEST: https://pds.ontempo.social/.well-known/oauth-authorization-server
METHOD: GET
COMMON HEADERS
-> Accept: application/json
-> Accept-Charset: UTF-8
CONTENT HEADERS
-> Content-Length: 0
BODY Content-Type: null
BODY START
BODY END
2025-12-09 22:14:07.489 26589-26589 HTTP co.ontempo.tempo D RESPONSE: 200 OK
METHOD: GET
FROM: https://pds.ontempo.social/.well-known/oauth-authorization-server
COMMON HEADERS
-> access-control-allow-headers: Content-Type,DPoP
-> access-control-allow-methods: *
-> access-control-allow-origin: *
-> access-control-max-age: 86400
-> cache-control: max-age=300
-> connection: keep-alive
-> content-type: application/json
-> date: Wed, 10 Dec 2025 03:14:05 GMT
-> server: openresty
-> strict-transport-security: max-age=63072000; preload
-> transfer-encoding: chunked
-> vary: Accept-Encoding
-> x-android-received-millis: 1765336447481
-> x-android-response-source: NETWORK 200
-> x-android-selected-protocol: http/1.1
-> x-android-sent-millis: 1765336447467
-> x-powered-by: Express
-> x-served-by: pds.ontempo.social
BODY Content-Type: application/json
BODY START
{"issuer":"https://ontempo.social","request_parameter_supported":true,"request_uri_parameter_supported":true,"require_request_uri_registration":true,"scopes_supported":["atproto","transition:email","transition:generic","transition:chat.bsky"],"subject_types_supported":["public"],"response_types_supported":["code"],"response_modes_supported":["query","fragment","form_post"],"grant_types_supported":["authorization_code","refresh_token"],"code_challenge_methods_supported":["S256"],"ui_locales_supported":["en-US"],"display_values_supported":["page","popup","touch"],"request_object_signing_alg_values_supported":["RS256","RS384","RS512","PS256","PS384","PS512","ES256","ES256K","ES384","ES512","none"],"authorization_response_iss_parameter_supported":true,"request_object_encryption_alg_values_supported":[],"request_object_encryption_enc_values_supported":[],"jwks_uri":"https://ontempo.social/oauth/jwks","authorization_endpoint":"https://ontempo.social/oauth/authorize","token_endpoint":"https://ontempo.social/oauth/token","token_endpoint_auth_methods_supported":["none","private_key_jwt"],"token_endpoint_auth_signing_alg_values_supported":["RS256","RS384","RS512","PS256","PS384","PS512","ES256","ES256K","ES384","ES512"],"revocation_endpoint":"https://ontempo.social/oauth/revoke","pushed_authorization_request_endpoint":"https://ontempo.social/oauth/par","require_pushed_authorization_requests":true,"dpop_signing_alg_values_supported":["RS256","RS384","RS512","PS256","PS384","PS512","ES256","ES256K","ES384","ES512"],"protected_resources":["https://ontempo.social"],"client_id_metadata_document_supported":true}
BODY END
2025-12-09 22:14:07.494 26589-26589 AtProtoAuth co.ontempo.tempo D Retrieved OAuth metadata from issuer: https://ontempo.social
2025-12-09 22:14:07.500 26589-26589 AtProtoAuth co.ontempo.tempo D Generated DPoP key pair (keyId: kzbcqvp3yp7Z_RSh)
2025-12-09 22:14:07.500 26589-26589 System.out co.ontempo.tempo I Generated code_verifier: length=43
2025-12-09 22:14:07.501 26589-26589 AtProtoAuth co.ontempo.tempo D Initiating PAR request to https://ontempo.social/oauth/par
2025-12-09 22:14:07.513 26589-26673 HTTP co.ontempo.tempo D REQUEST: https://ontempo.social/oauth/par
METHOD: POST
COMMON HEADERS
-> Accept: application/json
-> Accept-Charset: UTF-8
-> DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiWDhuVEtVNXJreHpMWGMzUzRwaUs4clNBWEd4Q252STIyYWFtVHRhWW5LMCIsInkiOiJqOGNQYXFwdDc4X0hxT3hqNTU2cWpqbTJ6QVpaSzVLN2d2UmlBQTJWT3gwIn19.eyJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9vbnRlbXBvLnNvY2lhbC9vYXV0aC9wYXIiLCJpYXQiOjE3NjUzMzY0NDcsImp0aSI6Ijk1Nzc2M2EyYjIzMDk1NjYxOTYwNzVkY2NjZjFmYzEzIn0.lSMPhraU1iwPEKfhFZnRqU7jJbr7E2uYrRWq5qq4H_9nF2iiQnH5JDrWHXF3py98Nhftit0PXjeJBZqzlUO_1g
CONTENT HEADERS
-> Content-Length: 387
-> Content-Type: application/x-www-form-urlencoded; charset=UTF-8
BODY Content-Type: application/x-www-form-urlencoded; charset=UTF-8
BODY START
client_id=https%3A%2F%2Fauth.ontempo.social%2Fnative%2Fmetadata%2Foauth-client-metadata.json&code_challenge=J6BWB34eA_TK3vBqeJVJxjvIBvBF3jUm9rKeXP3fyII&code_challenge_method=S256&redirect_uri=https%3A%2F%2Fauth.ontempo.social%2Fnative%2Fcallback%2Fcallback.html&response_type=code&scope=atproto+transition%3Ageneric&state=3c3a8b89a89f6d85d8aeb63f2d9198e5&login_hint=nackko.ontempo.social
BODY END
2025-12-09 22:14:07.585 26589-26589 HTTP co.ontempo.tempo D RESPONSE: 400 Bad Request
METHOD: POST
FROM: https://ontempo.social/oauth/par
COMMON HEADERS
-> access-control-allow-headers: Content-Type,DPoP
-> access-control-allow-methods: *
-> access-control-allow-origin: *
-> access-control-expose-headers: DPoP-Nonce
-> access-control-max-age: 86400
-> cache-control: no-store
-> connection: keep-alive
-> content-type: application/json
-> date: Wed, 10 Dec 2025 03:14:05 GMT
-> dpop-nonce: _aI36T_afmH32_vOPANVF9PTt1WtG0OSEIwr1TgG0qA
-> pragma: no-cache
-> server: openresty
-> strict-transport-security: max-age=63072000; preload
-> transfer-encoding: chunked
-> vary: Accept-Encoding
-> x-android-received-millis: 1765336447577
-> x-android-response-source: NETWORK 400
-> x-android-selected-protocol: http/1.1
-> x-android-sent-millis: 1765336447565
-> x-powered-by: Express
BODY Content-Type: application/json
BODY START
{"error":"use_dpop_nonce","error_description":"Authorization server requires nonce in DPoP proof"}
BODY END
2025-12-09 22:14:07.588 26589-26674 HTTP co.ontempo.tempo D REQUEST: https://ontempo.social/oauth/par
METHOD: POST
COMMON HEADERS
-> Accept: application/json
-> Accept-Charset: UTF-8
-> DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiWDhuVEtVNXJreHpMWGMzUzRwaUs4clNBWEd4Q252STIyYWFtVHRhWW5LMCIsInkiOiJqOGNQYXFwdDc4X0hxT3hqNTU2cWpqbTJ6QVpaSzVLN2d2UmlBQTJWT3gwIn19.eyJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9vbnRlbXBvLnNvY2lhbC9vYXV0aC9wYXIiLCJpYXQiOjE3NjUzMzY0NDcsImp0aSI6Ijk4YTQxZGNmYWI0MThhZWMyNDNjYzEyYTg0ZGM4ZmJmIiwibm9uY2UiOiJfYUkzNlRfYWZtSDMyX3ZPUEFOVkY5UFR0MVd0RzBPU0VJd3IxVGdHMHFBIn0.oWMNNbJoaOk5LIggFGTFZdnaRVnr4NEexj14qTD2kS-PbKwU82vAC6phBupf24gEPW3xLO8MHWgihUqVMAgEWA
CONTENT HEADERS
-> Content-Length: 387
-> Content-Type: application/x-www-form-urlencoded; charset=UTF-8
BODY Content-Type: application/x-www-form-urlencoded; charset=UTF-8
BODY START
client_id=https%3A%2F%2Fauth.ontempo.social%2Fnative%2Fmetadata%2Foauth-client-metadata.json&code_challenge=J6BWB34eA_TK3vBqeJVJxjvIBvBF3jUm9rKeXP3fyII&code_challenge_method=S256&redirect_uri=https%3A%2F%2Fauth.ontempo.social%2Fnative%2Fcallback%2Fcallback.html&response_type=code&scope=atproto+transition%3Ageneric&state=3c3a8b89a89f6d85d8aeb63f2d9198e5&login_hint=nackko.ontempo.social
BODY END
2025-12-09 22:14:07.609 26589-26589 HTTP co.ontempo.tempo D RESPONSE: 201 Created
METHOD: POST
FROM: https://ontempo.social/oauth/par
COMMON HEADERS
-> access-control-allow-headers: Content-Type,DPoP
-> access-control-allow-methods: *
-> access-control-allow-origin: *
-> access-control-expose-headers: DPoP-Nonce
-> access-control-max-age: 86400
-> cache-control: no-store
-> connection: keep-alive
-> content-type: application/json
-> date: Wed, 10 Dec 2025 03:14:05 GMT
-> dpop-nonce: _aI36T_afmH32_vOPANVF9PTt1WtG0OSEIwr1TgG0qA
-> pragma: no-cache
-> server: openresty
-> strict-transport-security: max-age=63072000; preload
-> transfer-encoding: chunked
-> vary: Accept-Encoding
-> x-android-received-millis: 1765336447604
-> x-android-response-source: NETWORK 201
-> x-android-selected-protocol: http/1.1
-> x-android-sent-millis: 1765336447592
-> x-powered-by: Express
-> x-served-by: ontempo.social
BODY Content-Type: application/json
BODY START
{"request_uri":"urn:ietf:params:oauth:request_uri:req-1db1d3f35c68a33670345c02c52473b8","expires_in":299}
BODY END
2025-12-09 22:14:07.612 26589-26589 AtProtoAuth co.ontempo.tempo D PAR request successful, received request_uri
2025-12-09 22:14:07.612 26589-26589 System.out co.ontempo.tempo I Authorization URL: https://ontempo.social/oauth/authorize?client_id=https://auth.ontempo.social/native/metadata/oauth-client-metadata.json&request_uri=urn:ietf:params:oauth:request_uri:req-1db1d3f35c68a33670345c02c52473b8
2025-12-09 22:14:07.612 26589-26589 AtProtoAuth co.ontempo.tempo D Opening browser for user authorization
2025-12-09 22:14:07.769 26589-26589 ImeBackDispatcher co.ontempo.tempo D Clear (mImeCallbacks.size=0)
2025-12-09 22:14:07.769 26589-26589 ImeBackDispatcher co.ontempo.tempo D switch root view (mImeCallbacks.size=0)
2025-12-09 22:14:08.216 26589-26589 VRI[MainActivity] co.ontempo.tempo D visibilityChanged oldVisibility=true newVisibility=false
2025-12-09 22:14:08.229 26589-26605 HWUI co.ontempo.tempo D endAllActiveAnimators on 0xb4000071d7e56ee0 (UnprojectedRipple) with handle 0xb4000070a7e6c4e0
2025-12-09 22:14:08.245 26589-26589 AutofillManager co.ontempo.tempo I onInvisibleForAutofill(): expiringResponse
2025-12-09 22:14:12.286 26589-26589 AtProtoAuth co.ontempo.tempo D Received redirect from authorization server
2025-12-09 22:14:12.286 26589-26589 AtProtoAuth co.ontempo.tempo D State validation successful
2025-12-09 22:14:12.286 26589-26589 AtProtoAuth co.ontempo.tempo D === Authorization code extracted from redirect === cod-aff4afba08ecb564493411803208257eeff1853e4092320047a101b70cda2a9e
2025-12-09 22:14:12.286 26589-26589 AtProtoAuth co.ontempo.tempo D Exchanging authorization code for tokens
2025-12-09 22:14:12.292 26589-26674 HTTP co.ontempo.tempo D REQUEST: https://ontempo.social/oauth/token
METHOD: POST
COMMON HEADERS
-> Accept: application/json
-> Accept-Charset: UTF-8
-> DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiWDhuVEtVNXJreHpMWGMzUzRwaUs4clNBWEd4Q252STIyYWFtVHRhWW5LMCIsInkiOiJqOGNQYXFwdDc4X0hxT3hqNTU2cWpqbTJ6QVpaSzVLN2d2UmlBQTJWT3gwIn19.eyJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9vbnRlbXBvLnNvY2lhbC9vYXV0aC90b2tlbiIsImlhdCI6MTc2NTMzNjQ1MiwianRpIjoiMDZiNTgzYWU2NDM4MDkwMDMzNzIzYWI0ODlmNjNmNDMifQ.vwQIYg4cI2X_MK5N4WPwqsJyxVIu7g8H4QGDP1MmZ1-SZE3btWR0L2BrPJ5HfLBLM06kB6BqcwG1KCSP6F-_6g
CONTENT HEADERS
-> Content-Length: 337
-> Content-Type: application/x-www-form-urlencoded; charset=UTF-8
BODY Content-Type: application/x-www-form-urlencoded; charset=UTF-8
BODY START
grant_type=authorization_code&code=cod-aff4afba08ecb564493411803208257eeff1853e4092320047a101b70cda2a9e&code_verifier=OlhBuzK9zGTv9FEtAWJ9shdUD4_YEWLRjMaCtHwqMmY&redirect_uri=https%3A%2F%2Fauth.ontempo.social%2Fnative%2Fcallback%2Fcallback.html&client_id=https%3A%2F%2Fauth.ontempo.social%2Fnative%2Fmetadata%2Foauth-client-metadata.json
BODY END
2025-12-09 22:14:12.304 26589-26589 ViewRootImpl co.ontempo.tempo D Skipping stats log for color mode
2025-12-09 22:14:12.323 26589-26589 HTTP co.ontempo.tempo D RESPONSE: 400 Bad Request
METHOD: POST
FROM: https://ontempo.social/oauth/token
COMMON HEADERS
-> access-control-allow-headers: Content-Type,DPoP
-> access-control-allow-methods: *
-> access-control-allow-origin: *
-> access-control-expose-headers: DPoP-Nonce
-> access-control-max-age: 86400
-> cache-control: no-store
-> connection: keep-alive
-> content-type: application/json
-> date: Wed, 10 Dec 2025 03:14:10 GMT
-> dpop-nonce: _aI36T_afmH32_vOPANVF9PTt1WtG0OSEIwr1TgG0qA
-> pragma: no-cache
-> server: openresty
-> strict-transport-security: max-age=63072000; preload
-> transfer-encoding: chunked
-> vary: Accept-Encoding
-> x-android-received-millis: 1765336452308
-> x-android-response-source: NETWORK 400
-> x-android-selected-protocol: http/1.1
-> x-android-sent-millis: 1765336452299
-> x-powered-by: Express
BODY Content-Type: application/json
BODY START
{"error":"use_dpop_nonce","error_description":"Authorization server requires nonce in DPoP proof"}
BODY END
2025-12-09 22:14:12.328 26589-26673 HTTP co.ontempo.tempo D REQUEST: https://ontempo.social/oauth/token
METHOD: POST
COMMON HEADERS
-> Accept: application/json
-> Accept-Charset: UTF-8
-> DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiWDhuVEtVNXJreHpMWGMzUzRwaUs4clNBWEd4Q252STIyYWFtVHRhWW5LMCIsInkiOiJqOGNQYXFwdDc4X0hxT3hqNTU2cWpqbTJ6QVpaSzVLN2d2UmlBQTJWT3gwIn19.eyJodG0iOiJQT1NUIiwiaHR1IjoiaHR0cHM6Ly9vbnRlbXBvLnNvY2lhbC9vYXV0aC90b2tlbiIsImlhdCI6MTc2NTMzNjQ1MiwianRpIjoiNzlkMjZlYjVlOTg2YjMwZGFmZDVkMDUxODdhN2NlMjMiLCJub25jZSI6Il9hSTM2VF9hZm1IMzJfdk9QQU5WRjlQVHQxV3RHME9TRUl3cjFUZ0cwcUEifQ.f_xU1HO-rnVRkxZzqMj7gq-Qa8pW6Swm1jh2UbBLVZXU6Ykc8eX0Bqhil7j6uFICvCMDF3LaaxVqbN6iJ1031g
CONTENT HEADERS
-> Content-Length: 337
-> Content-Type: application/x-www-form-urlencoded; charset=UTF-8
BODY Content-Type: application/x-www-form-urlencoded; charset=UTF-8
BODY START
grant_type=authorization_code&code=cod-aff4afba08ecb564493411803208257eeff1853e4092320047a101b70cda2a9e&code_verifier=OlhBuzK9zGTv9FEtAWJ9shdUD4_YEWLRjMaCtHwqMmY&redirect_uri=https%3A%2F%2Fauth.ontempo.social%2Fnative%2Fcallback%2Fcallback.html&client_id=https%3A%2F%2Fauth.ontempo.social%2Fnative%2Fmetadata%2Foauth-client-metadata.json
BODY END
2025-12-09 22:14:12.353 26589-26589 HTTP co.ontempo.tempo D RESPONSE: 400 Bad Request
METHOD: POST
FROM: https://ontempo.social/oauth/token
COMMON HEADERS
-> access-control-allow-headers: Content-Type,DPoP
-> access-control-allow-methods: *
-> access-control-allow-origin: *
-> access-control-expose-headers: DPoP-Nonce
-> access-control-max-age: 86400
-> cache-control: no-store
-> connection: keep-alive
-> content-type: application/json
-> date: Wed, 10 Dec 2025 03:14:10 GMT
-> dpop-nonce: _aI36T_afmH32_vOPANVF9PTt1WtG0OSEIwr1TgG0qA
-> pragma: no-cache
-> server: openresty
-> strict-transport-security: max-age=63072000; preload
-> transfer-encoding: chunked
-> vary: Accept-Encoding
-> x-android-received-millis: 1765336452347
-> x-android-response-source: NETWORK 400
-> x-android-selected-protocol: http/1.1
-> x-android-sent-millis: 1765336452334
-> x-powered-by: Express
BODY Content-Type: application/json
BODY START
{"error":"invalid_grant","error_description":"Invalid code"}
BODY END
2025-12-09 22:14:12.356 26589-26589 HTTP co.ontempo.tempo D RESPONSE https://ontempo.social/oauth/token failed with exception: io.ktor.serialization.JsonConvertException: Illegal input: Fields [access_token, refresh_token, expires_in, scope, sub] are required for type with serial name 'co.ontempo.tempo.framework.authentication.dto.AtProtoCredentialsDto', but they were missing at path: $
2025-12-09 22:14:12.356 26589-26589 AtProtoAuth co.ontempo.tempo E Authentication failed for handle: nackko.ontempo.social
java.lang.Exception: Illegal input: Fields [access_token, refresh_token, expires_in, scope, sub] are required for type with serial name 'co.ontempo.tempo.framework.authentication.dto.AtProtoCredentialsDto', but they were missing at path: $
While I was banging my head, I tried to copy the pds database after the code issuance but before trying to exchange it and got confused as to why the code was not in the database. Here is a summary of my findings with Claude (I am not a db guy hehe)
Investigation Steps Performed
Test 1: Database State Before Authentication
SELECT COUNT(*) FROM authorization_request;
-- Result: 2 (old expired requests)
SELECT COUNT(*) FROM token;
-- Result: 2
SELECT COUNT(*) FROM device_account;
-- Result: 0Test 2: Database State After Code Received
Immediately after receiving authorization code cod-909e11ef897e3a4e589c228d7400da93a7e40c9044ceff559de6f3517ecb8b10:
SELECT * FROM authorization_request WHERE code = 'cod-909e...';
-- Result: EMPTY
SELECT * FROM token WHERE code = 'cod-909e...';
-- Result: EMPTY
SELECT COUNT(*) FROM authorization_request;
-- Result: 0 (old requests cleaned up)
SELECT COUNT(*) FROM device_account;
-- Result: 0 (still empty!)Test 3: Evidence of PDS Activity
SELECT * FROM authorized_client
WHERE clientId = 'https://auth.ontempo.social/native/metadata/client-metadata.json';
-- Result: Record exists with updatedAt: 2025-12-01T03:18:20.801Z (during authentication attempt)
SELECT * FROM device ORDER BY lastSeenAt DESC LIMIT 1;
-- Result: dev-62584ad3b9be1725075f4e55dce5a74b with lastSeenAt: 2025-12-01T03:18:19.101Z
SELECT * FROM device_account WHERE deviceId = 'dev-62584ad3b9be1725075f4e55dce5a74b';
-- Result: EMPTYTest 4: Account and Actor Verification
SELECT * FROM account WHERE did = 'did:plc:syldnchxgxctqcjvkhqprrxz';
-- Result: Account exists with email nackko@***
SELECT * FROM actor WHERE did = 'did:plc:syldnchxgxctqcjvkhqprrxz';
-- Result: Actor exists with handle nackko.ontempo.socialAgain I got help from the Discord so feel free to close this issue if it's not relevant.
Thks for all the great work!
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels