Error: 401 /api/v1/accounts/token {"code":"invalid_client","description":"The specified client credentials are invalid."}

I Create a Confidential client with client secret. If i try to create an integration with this client, i get an error:

INFO - 172.25.0.1:41240 - "OPTIONS /api/v1/awork/oauth/url HTTP/1.1" 200
CRITICAL - url: https://api.awork.com/api/v1/accounts/authorize?client_id=***&response_type=code&grant_type=authorization_code&redirect_uri=***&state=***&scope=offline_access+full_access&code_challenge=***&code_challenge_method=S256
INFO - 172.25.0.1:41240 - "GET /api/v1/awork/oauth/url HTTP/1.1" 200
CRITICAL - --------------------------------------------------
CRITICAL - data: {'redirect_uri': '***/api/v1/awork/oauth/callback', 'grant_type': 'authorization_code', 'code': '***', 'code_verifier': '***'}
CRITICAL - headers: {'Authorization': 'Basic ***', 'Content-Type': 'application/x-www-form-urlencoded'}
CRITICAL - auth_base_url: https://api.awork.com/api/v1
CRITICAL - redirect_uri: ***/api/v1/awork/oauth/callback
CRITICAL - --------------------------------------------------
ERROR - HTTP Error: 401 Client Error: Unauthorized for url: https://api.awork.com/api/v1/accounts/token
ERROR - Response content: {"code":"invalid_client","description":"The specified client credentials are invalid."}

code snippets:

credentials = f"{self.client_id}:{self.client_secret}"
encoded = base64.b64encode(credentials.encode()).decode()
headers = {"Authorization": f"Basic {encoded}", ...}
generate_pkce_pair(self) -> Tuple[str, str]:
        """
        Generate PKCE code verifier and code challenge pair.

        Returns:
            Tuple of (code_verifier, code_challenge)
        """
        # Generate a cryptographically random code verifier
        self.code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8').rstrip('=')

        # Generate code challenge using SHA256
        code_challenge_bytes = hashlib.sha256(self.code_verifier.encode('utf-8')).digest()
        self.code_challenge = base64.urlsafe_b64encode(code_challenge_bytes).decode('utf-8').rstrip('=')

        return self.code_verifier, self.code_challenge

Intresting thing is, if i recreate the Client-Secret, the integration works:

INFO - 172.25.0.1:35920 - "OPTIONS /api/v1/awork/oauth/url HTTP/1.1" 200
CRITICAL - url: https://api.awork.com/api/v1/accounts/authorize?client_id=***&response_type=code&grant_type=authorization_code&redirect_uri=***&state=***&scope=offline_access+full_access&code_challenge=***&code_challenge_method=S256
INFO - 172.25.0.1:35920 - "GET /api/v1/awork/oauth/url HTTP/1.1" 200
CRITICAL - --------------------------------------------------
CRITICAL - data: {'redirect_uri': '***/api/v1/awork/oauth/callback', 'grant_type': 'authorization_code', 'code': '***', 'code_verifier': '***'}
CRITICAL - headers: {'Authorization': 'Basic ***', 'Content-Type': 'application/x-www-form-urlencoded'}
CRITICAL - auth_base_url: https://api.awork.com/api/v1
CRITICAL - redirect_uri: ***/api/v1/awork/oauth/callback
CRITICAL - --------------------------------------------------
CRITICAL - ++++++++++++++++++++++++++++++++++++++++++++++++++
CRITICAL - No refresh token in response!
CRITICAL - Full token response: {'access_token': '***.***.***', 'token_type': 'Bearer', 'expires_in': 86400}
CRITICAL - ++++++++++++++++++++++++++++++++++++++++++++++++++

Can anyone reproduce this? Or is my code wrong?
Interesting thing is, that i don’t get a refresh token in the response, which means, it is an „Public Client“ instead of a „Confidential Client“?

Last week it worked without any problems. In an attempt to fix my code, I added PKCE — maybe this is the problem, but I’m not sure.

Would appreciate any help!

Hi @Ole and thanks for reporting this. I was able to reproduce this, it seems like the initially generated client secret does not work. In my case it also works if I regenerate it. I will look into the issue.

To get a refresh token, you need to include the offline_access in the scope list so that the user knows you want continuous access. Hope this helps.

Hi @Sebastian, thank you for your reply. I request offline_access and full_access as you can see here:
CRITICAL - url: https://api.awork.com/api/v1/accounts/authorize?client_id=***&response_type=code&grant_type=authorization_code&redirect_uri=***&state=***&scope=offline_access+full_access&code_challenge=***&code_challenge_method=S256

I also tried requesting only offline_access, but I still don’t get a refresh_token. Until last week, I was receiving the refresh_token.

Thanks for your help :slightly_smiling_face:

Thanks, I was able to reproduce the issue. A refresh_token was only issued the first time the user authorized the app. We now store the authorization so this step can be skipped, leading to the refresh token not being issued in those cases. A fix is on the way and should be available tomorrow.

1 „Gefällt mir“

Hey @Sebastian,

I came here to write exactly that. I had no problems with a new app, but ran into the refresh_token issue the second time I requested a key. So thanks a lot for reproducing and fixing my issues :star_struck:

Thanks,
Ole