tableau / connector-plugin-sdk

SDK for Developing Tableau Connector Plugins
https://tableau.github.io/connector-plugin-sdk/
MIT License
108 stars 108 forks source link

Oauth Token Expires Prematurely #1260

Open doulam opened 5 days ago

doulam commented 5 days ago

Name: Douglas Lma Company: Netflix

Hi Team,

We built a custom Tableau Connector with Oauth. From the data connectivity front, it is working as expected. We were able to authenticate and connect to our backend from Desktop and Tableau Server to fetch the data.

We are noticing that the OAuth token expires more frequently than it should. The token is set to expire every 30 days, but on Tableau Server, it expires within 2 days from the token issued. Is there any known issue with the token created by the custom connector on the server? Any suggestions on how to troubleshoot this problem?

I saw these error messages in my log.

ERROR 2024-10-02 18:05:08.003 +0000 grpc-default-executor-0 : com.tableau.oauth.grpc.OAuthV1GrpcAdapter - Received exception while attempting to refresh accessToken com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request POST https:/***/as/token.oauth2 { "error": "invalid_grant", "error_description": "unknown, invalid, or expired refresh token" } at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:103) ~[google-oauth-client-1.34.1.jar!/:1.34.1]

Here is my oauth XML:

/as/authorization.oauth2
<tokenUri>/as/token.oauth2</tokenUri>

<instanceUrlValidationRegex>^https://****.netflix.com(.*)</instanceUrlValidationRegex>
<scopes>default</scopes>
<capabilities>
    <entry>
        <key>OAUTH_CAP_SUPPORTS_CUSTOM_DOMAIN</key>
        <value>true</value>
    </entry>
    <entry>
        <key>OAUTH_CAP_FIXED_PORT_IN_CALLBACK_URL</key>
        <value>true</value>
    </entry>
    <entry>
        <key>OAUTH_CAP_SUPPORTS_GET_USERINFO_FROM_ID_TOKEN</key>
        <value>true</value>
    </entry>
    <entry>
        <key>OAUTH_CAP_CLIENT_SECRET_IN_URL_QUERY_PARAM</key>
        <value>true</value>
    </entry>
</capabilities>

<!-- Map Tableau recognized attribute "key" to OAuth response attribute "value" -->
<accessTokenResponseMaps>
    <entry>
        <key>ACCESSTOKEN</key>
        <value>access_token</value>
    </entry>
    <entry>
        <key>REFRESHTOKEN</key>
        <value>refresh_token</value>
    </entry>
    <entry>
        <key>access-token-issue-time</key>
        <value>iat</value>
    </entry>
    <entry>
        <key>access-token-expires-in</key>
        <value>expires_in</value>
    </entry>
    <entry>
        <key>username</key>
        <value>sub</value>
    </entry>
    <entry>
        <key>id-token</key>
        <value>id_token</value>
    </entry>
</accessTokenResponseMaps>

<!-- No refreshTokenResponseMaps needed since it's the same as accessTokenResponseMaps -->

doulam commented 5 days ago

More error from log.

{"ts":"2024-10-02T18:05:08.005","pid":32916,"tid":"b63c","sev":"warn","req":"-","sess":"-","site":"-","user":"-","k":"excp","e":{"excp-error-code":"0x84223ADA","excp-source":"Client","excp-status-code":"UNKNOWN"},"v":{"class":"","connectivity_stage":"ConnectivityStage::Unknown","dse-type":"OAuthFailure","excp-msg":"Tableau received an OAuth error from your request. Please see the error message for more information: 400 Bad Request\nPOST https://****.netflix.com/as/token.oauth2\n{\n \"error\": \"invalid_grant\",\n \"error_description\": \"unknown, invalid, or expired refresh token\"\n}.\n","excp-type":"ConnectivityException","is-bad-request":false,"is-capability-probe-failure":false,"is-local-configuration-error":false,"is-remote-configuration-error":false,"msg":"ConnectivityException::Init","server_version":"","sql_state":""}} {"ts":"2024-10-02T18:05:08.005","pid":32916,"tid":"b63c","sev":"info","req":"-","sess":"-","site":"-","user":"-","k":"msg","v":"Unable to Refresh the Access Token. Check the error details."} {"ts":"2024-10-02T18:05:08.005","pid":32916,"tid":"b63c","sev":"info","req":"-","sess":"-","site":"-","user":"-","k":"msg","v":"Client ID: tableau_oauth_client - RefreshToken is empty? 0 - Service Account is empty? 1 - AccessToken is empty? 1 - Expires in:"} {"ts":"2024-10-02T18:05:08.005","pid":32916,"tid":"b63c","sev":"warn","req":"-","sess":"-","site":"-","user":"-","k":"excp","e":{"excp-error-code":"0xFF9C14D9","excp-source":"Client","excp-status-code":"UNAUTHENTICATED"},"v":{"class":"","connectivity_stage":"ConnectivityStage::Unknown","dse-type":"ExpiredOAuthRefreshToken","excp-msg":"Tableau detected that your OAuth refresh token is expired. Reauthenticate with new credentials. Ask your Tableau admin if you need help.\n","excp-type":"ConnectivityException","is-bad-request":false,"is-capability-probe-failure":false,"is-local-configuration-error":false,"is-remote-configuration-error":false,"msg":"ConnectivityException::Init","server_version":"","sql_state":""}} {"ts":"2024-10-02T18:05:08.005","pid":32916,"tid":"b63c","sev":"warn","req":"-","sess":"-","site":"-","user":"-","k":"msg","v":"Invalid refresh token."}

jkoskela commented 5 days ago

What is your IDP? Is there a limit on the number of active tokens? Have you asked the team that manages the IDP if they can see why that request failed?

If you look in the oauthservice log, there may be more useful information, but probably not. I expect that something is invalidating those tokens on the IDP, and that it's nothing to do with us.

You don't have single use refresh tokens do you? Like when we refresh the access token, it doesn't return a new refresh token also?

doulam commented 3 days ago

Hello @jkoskela thanks for your response.

We are using PingFederate and are in the process of transitioning to Okta. Yes, I've been working with our IDP team to find the problem. Do you know if we can enable the log on Tableau Server to capture the response header log for the Oauth? Seems like the Access Token was missing when it called out IDP. I am trying to determine that. We are able to reproduce the error with this curl:

curl -si --location 'https://idp.netflix.com/as/token.oauth2' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=refresh_token' \ --data-urlencode 'refresh_token=fake'
HTTP/2 400 content-type: application/json;charset=utf-8 date: Thu, 03 Oct 2024 18:13:30 GMT x-frame-options: SAMEORIGIN referrer-policy: origin cache-control: no-cache, no-store pragma: no-cache expires: Thu, 01 Jan 1970 00:00:00 GMT vary: Origin x-cache: Error from cloudfront via: 1.1 3a60765023a93f6346539d2ca40f0b12.cloudfront.net (CloudFront) x-amz-cf-pop: IAD61-P2 x-amz-cf-id: GfVKXrnw3X4_7lHgiK_ubz_kfKfqaNzZleXf29-93oMB9lTwTHk-Iw==

{"error_description":"unknown, invalid, or expired refresh token","error":"invalid_grant"}

It worked for about 2 hours while creating the published data source using the new connector with OAuth before prompting again to sign up for a new token. Our refresh token lasts for 30 days.