<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: Issues with Refresh Token Handling in OAuth 2.0 in Developer</title>
    <link>https://community.fabric.microsoft.com/t5/Developer/Issues-with-Refresh-Token-Handling-in-OAuth-2-0/m-p/4026808#M53956</link>
    <description>&lt;P&gt;Hi&lt;BR /&gt;I have a similar problem and I hope you might be able to help.&lt;BR /&gt;&lt;BR /&gt;I have a custom connector that works. It authenticates to Auth0 and gets a token that expires in 24 hours. The refresh token method also works and the expired token is refreshed.&lt;BR /&gt;&lt;BR /&gt;My problem is that the "refresh_token" itself expires after 7 days and even though my Auth0 is set up to rotate refresh tokens, something is not working there. The refresh token expires and I have to do a manual refresh (re-sign-in to generate a new token) before 7 days in order for the next 7 days of refreshing kicks in.&lt;BR /&gt;&lt;BR /&gt;So, my question is this: what do I need to do to get the rotated refresh token to be used?&amp;nbsp;&lt;BR /&gt;The bit about all this that puzzles me is that even though the refresh token rotation is enabled on Auth0, I guess it is not really rotating the refresh token because, if it was, the second attempt to refresh using the old refresh token would fail.&lt;BR /&gt;&lt;BR /&gt;At any rate, the symptoms of what is happening seems to indicate that the refresh token is not rotating and the old refresh token is reused until it expires (in 7 days in my case)&lt;BR /&gt;Here is my code if it helps any:&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;StartLogin = (dataSourcePath, state, display) =&amp;gt;
    let
        json = Json.Document(dataSourcePath),
        codeVerifier = Text.NewGuid() &amp;amp; Text.NewGuid(),
        codeChallenge = Base64UrlEncodeWithoutPadding(Crypto.CreateHash(CryptoAlgorithm.SHA256, Text.ToBinary(codeVerifier, TextEncoding.Ascii))),

        AuthorizeUrl = json[auth_url] &amp;amp; "?" &amp;amp; Uri.BuildQueryString([
            client_id = json[client_id],
            state = state,
            redirect_uri = redirect_uri,
            scope = DataMosaixDefaults[Scope],//json[scope],
            audience = DataMosaixDefaults[Audience],//json[audience],
            code_challenge_method = "S256",
            code_challenge = codeChallenge,
            response_type = "code"])
    in
        [
            LoginUri = AuthorizeUrl,
            CallbackUri = redirect_uri,
            WindowHeight = windowHeight,
            WindowWidth = windowWidth,
            Context = [code_verifier = codeVerifier]
        ];

FinishLogin = (clientApplication, dataSourcePath, context, callbackUri, state) =&amp;gt;
    let
        json = Json.Document(dataSourcePath),
        Parts = Uri.Parts(callbackUri)[Query]
    in
        TokenMethod("authorization_code", json[deployment], Parts[code], json[token_url], json[client_id], context);

Refresh = (dataSourcePath, refreshToken, oldCredential) =&amp;gt;
    let
        json = Json.Document(dataSourcePath),
        refreshToken = oldCredential[refresh_token],
        result = TokenMethod("refresh_token", json[deployment], refreshToken, json[token_url], json[client_id])
    in
        result;

TokenMethod = (grant_type, deployment, authCode, token_url, client_id, optional context) =&amp;gt;
    let
        CodeVerifier = if (context &amp;lt;&amp;gt; null) then [code_verifier = context[code_verifier]] else [],
        Parameters = if (grant_type = "authorization_code") then [ redirect_uri = redirect_uri, code = authCode ] else [ refresh_token = authCode ],
        Response = Web.Contents(token_url, [
            Content = Text.ToBinary(Uri.BuildQueryString([
                client_id = client_id,
                grant_type = grant_type,
                redirect_uri = redirect_uri] &amp;amp; Parameters &amp;amp; CodeVerifier)),
            Headers=[#"Content-type" = "application/x-www-form-urlencoded",#"Accept" = "application/json"]]),
        RefreshToken = if (grant_type = "authorization_code") then [] else [ refresh_token = authCode ],
        Parts = Json.Document(Response)
    in
        Parts;&lt;/LI-CODE&gt;</description>
    <pubDate>Fri, 05 Jul 2024 09:48:17 GMT</pubDate>
    <dc:creator>apdams</dc:creator>
    <dc:date>2024-07-05T09:48:17Z</dc:date>
    <item>
      <title>Issues with Refresh Token Handling in OAuth 2.0</title>
      <link>https://community.fabric.microsoft.com/t5/Developer/Issues-with-Refresh-Token-Handling-in-OAuth-2-0/m-p/3996769#M53458</link>
      <description>&lt;P&gt;Hello everyone,&lt;/P&gt;&lt;P&gt;I'm encountering a persistent issue with handling refresh tokens in my custom Power BI connector, and I could use some expert advice.&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;Problem Description:&lt;/STRONG&gt;&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;I building a custom Power BI connector that uses OAuth 2.0 PKCE for authentication.&lt;/LI&gt;&lt;LI&gt;The initial access token is retrieved successfully, but I'm running into problems when trying to use the refresh token to obtain a new access token.&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;&lt;STRONG&gt;Details:&lt;/STRONG&gt;&lt;/P&gt;&lt;OL&gt;&lt;LI&gt;&lt;P&gt;&lt;STRONG&gt;Token and Expiration&lt;/STRONG&gt;: Along with the access token, we receive an expiration time and a refresh token.&lt;/P&gt;&lt;/LI&gt;&lt;LI&gt;&lt;STRONG&gt;Long Data Fetch Duration&lt;/STRONG&gt;: Fetching data takes more than 1 hour, during which the access token expires, necessitating a new access token.&lt;/LI&gt;&lt;LI&gt;&lt;P&gt;&lt;STRONG&gt;Refresh Token Request Issue&lt;/STRONG&gt;: When attempting to use the refresh token, the refresh request is sent twice in quick succession:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;The first request is successful and returns a new access token.&lt;/LI&gt;&lt;LI&gt;The second request fails because it tries to use the same refresh token that was just invalidated by the first successful request.(Checking all the requests in Fiddler)&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P&gt;&lt;STRONG&gt;User Interruption&lt;/STRONG&gt;: This issue causes an interruption where a window is displayed asking users to re-enter their credentials while data is being fetched from the API.&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;&lt;/LI&gt;&lt;/OL&gt;&lt;P&gt;&lt;STRONG&gt;Current Implementation:&lt;BR /&gt;&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;StartLogin = (resourceUrl, state, display) =&amp;gt;
    let
        codeVerifier = Text.NewGuid() &amp;amp; Text.NewGuid(),
        AuthorizeUrl = authorizationUri
            &amp;amp; "?"
            &amp;amp; Uri.BuildQueryString(
                [
                    client_id = clientId,
                    response_type = "code",
                    code_challenge_method = "plain",
                    scope = scope,
                    code_challenge = codeVerifier,
                    state = state,
                    redirect_uri = callbackUri
                ]
            )
    in
        [
            LoginUri = AuthorizeUrl,
            CallbackUri = callbackUri,
            WindowHeight = 800,
            WindowWidth = 600,
            Context = codeVerifier
        ];

FinishLogin = (context, callbackUri, state) =&amp;gt;
    let
        Parts = Uri.Parts(callbackUri)[Query]
    in
        TokenMethod(Parts[code], "authorization_code", context);

TokenMethod = (code, grant_type, optional verifier) =&amp;gt;
    let
        codeVerifier = if (verifier &amp;lt;&amp;gt; null) then [code_verifier = verifier] else [],
        codeParameter = if (grant_type = "authorization_code") then [code = code] else [refresh_token = code],
        query = codeVerifier
            &amp;amp; codeParameter
            &amp;amp; [
                client_id = clientId,
                grant_type = grant_type,
                redirect_uri = callbackUri
            ],
        ManualHandlingStatusCodes = {400, 403},
        Response = Web.Contents(
            tokenUri,
            [
                Content = Text.ToBinary(Uri.BuildQueryString(query)),
                Headers = [
                    #"Content-type" = "application/x-www-form-urlencoded",
                    #"Accept" = "application/json"
                ],
                ManualStatusHandling = ManualHandlingStatusCodes
            ]
        ),
        Parts = Json.Document(Response)
    in
        // check for error in response
         if (Parts[error]? &amp;lt;&amp;gt; null) then
             error Error.Record(Parts[error], Parts[message]?)
         else
            Parts;

Refresh = (resourceUrl, refresh_token) =&amp;gt;
    TokenMethod(refresh_token, "refresh_token");

// Data Source Kind description
SampleConnector = [
    Authentication = [
        OAuth = [
            StartLogin = StartLogin,
            FinishLogin = FinishLogin,
            Refresh = Refresh
        ]
    ],
    Label = "Sample Connector"
];&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;STRONG&gt;&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 18 Jun 2024 04:08:05 GMT</pubDate>
      <guid>https://community.fabric.microsoft.com/t5/Developer/Issues-with-Refresh-Token-Handling-in-OAuth-2-0/m-p/3996769#M53458</guid>
      <dc:creator>Suraj_Ncircle</dc:creator>
      <dc:date>2024-06-18T04:08:05Z</dc:date>
    </item>
    <item>
      <title>Re: Issues with Refresh Token Handling in OAuth 2.0</title>
      <link>https://community.fabric.microsoft.com/t5/Developer/Issues-with-Refresh-Token-Handling-in-OAuth-2-0/m-p/3997479#M53469</link>
      <description>&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://community.fabric.microsoft.com/t5/user/viewprofilepage/user-id/710355"&gt;@Suraj_Ncircle&lt;/a&gt;&amp;nbsp;,&lt;/P&gt;
&lt;P&gt;It sounds like your custom Power BI connector is struggling with handling the refresh token properly, causing redundant requests and user interruptions. Please ensure that only one refresh token request is issued at a time:&lt;/P&gt;
&lt;LI-CODE lang="markup"&gt;shared Refresh = (resourceUrl, refresh_token) =&amp;gt;
    let
        // Ensure only one refresh request is in process at a time
        TokenRefresh = createTokenRefreshFunction(refresh_token),
        Result = TokenRefresh()
    in
        Result;

createTokenRefreshFunction = (refresh_token) =&amp;gt;
    let
        inProgress = false,
        cachedToken = null,
        
        refreshTokenFunction = () =&amp;gt;
            if inProgress then
                cachedToken
            else
                let
                    _ = (inProgress := true),
                    newToken = TokenMethod(refresh_token, "refresh_token"),
                    _ = (cachedToken := newToken),
                    _ = (inProgress := false)
                in
                    newToken
    in
        refreshTokenFunction;&lt;/LI-CODE&gt;
&lt;P&gt;Best Regards&lt;/P&gt;</description>
      <pubDate>Tue, 18 Jun 2024 05:43:47 GMT</pubDate>
      <guid>https://community.fabric.microsoft.com/t5/Developer/Issues-with-Refresh-Token-Handling-in-OAuth-2-0/m-p/3997479#M53469</guid>
      <dc:creator>Anonymous</dc:creator>
      <dc:date>2024-06-18T05:43:47Z</dc:date>
    </item>
    <item>
      <title>Re: Issues with Refresh Token Handling in OAuth 2.0</title>
      <link>https://community.fabric.microsoft.com/t5/Developer/Issues-with-Refresh-Token-Handling-in-OAuth-2-0/m-p/4026808#M53956</link>
      <description>&lt;P&gt;Hi&lt;BR /&gt;I have a similar problem and I hope you might be able to help.&lt;BR /&gt;&lt;BR /&gt;I have a custom connector that works. It authenticates to Auth0 and gets a token that expires in 24 hours. The refresh token method also works and the expired token is refreshed.&lt;BR /&gt;&lt;BR /&gt;My problem is that the "refresh_token" itself expires after 7 days and even though my Auth0 is set up to rotate refresh tokens, something is not working there. The refresh token expires and I have to do a manual refresh (re-sign-in to generate a new token) before 7 days in order for the next 7 days of refreshing kicks in.&lt;BR /&gt;&lt;BR /&gt;So, my question is this: what do I need to do to get the rotated refresh token to be used?&amp;nbsp;&lt;BR /&gt;The bit about all this that puzzles me is that even though the refresh token rotation is enabled on Auth0, I guess it is not really rotating the refresh token because, if it was, the second attempt to refresh using the old refresh token would fail.&lt;BR /&gt;&lt;BR /&gt;At any rate, the symptoms of what is happening seems to indicate that the refresh token is not rotating and the old refresh token is reused until it expires (in 7 days in my case)&lt;BR /&gt;Here is my code if it helps any:&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;StartLogin = (dataSourcePath, state, display) =&amp;gt;
    let
        json = Json.Document(dataSourcePath),
        codeVerifier = Text.NewGuid() &amp;amp; Text.NewGuid(),
        codeChallenge = Base64UrlEncodeWithoutPadding(Crypto.CreateHash(CryptoAlgorithm.SHA256, Text.ToBinary(codeVerifier, TextEncoding.Ascii))),

        AuthorizeUrl = json[auth_url] &amp;amp; "?" &amp;amp; Uri.BuildQueryString([
            client_id = json[client_id],
            state = state,
            redirect_uri = redirect_uri,
            scope = DataMosaixDefaults[Scope],//json[scope],
            audience = DataMosaixDefaults[Audience],//json[audience],
            code_challenge_method = "S256",
            code_challenge = codeChallenge,
            response_type = "code"])
    in
        [
            LoginUri = AuthorizeUrl,
            CallbackUri = redirect_uri,
            WindowHeight = windowHeight,
            WindowWidth = windowWidth,
            Context = [code_verifier = codeVerifier]
        ];

FinishLogin = (clientApplication, dataSourcePath, context, callbackUri, state) =&amp;gt;
    let
        json = Json.Document(dataSourcePath),
        Parts = Uri.Parts(callbackUri)[Query]
    in
        TokenMethod("authorization_code", json[deployment], Parts[code], json[token_url], json[client_id], context);

Refresh = (dataSourcePath, refreshToken, oldCredential) =&amp;gt;
    let
        json = Json.Document(dataSourcePath),
        refreshToken = oldCredential[refresh_token],
        result = TokenMethod("refresh_token", json[deployment], refreshToken, json[token_url], json[client_id])
    in
        result;

TokenMethod = (grant_type, deployment, authCode, token_url, client_id, optional context) =&amp;gt;
    let
        CodeVerifier = if (context &amp;lt;&amp;gt; null) then [code_verifier = context[code_verifier]] else [],
        Parameters = if (grant_type = "authorization_code") then [ redirect_uri = redirect_uri, code = authCode ] else [ refresh_token = authCode ],
        Response = Web.Contents(token_url, [
            Content = Text.ToBinary(Uri.BuildQueryString([
                client_id = client_id,
                grant_type = grant_type,
                redirect_uri = redirect_uri] &amp;amp; Parameters &amp;amp; CodeVerifier)),
            Headers=[#"Content-type" = "application/x-www-form-urlencoded",#"Accept" = "application/json"]]),
        RefreshToken = if (grant_type = "authorization_code") then [] else [ refresh_token = authCode ],
        Parts = Json.Document(Response)
    in
        Parts;&lt;/LI-CODE&gt;</description>
      <pubDate>Fri, 05 Jul 2024 09:48:17 GMT</pubDate>
      <guid>https://community.fabric.microsoft.com/t5/Developer/Issues-with-Refresh-Token-Handling-in-OAuth-2-0/m-p/4026808#M53956</guid>
      <dc:creator>apdams</dc:creator>
      <dc:date>2024-07-05T09:48:17Z</dc:date>
    </item>
  </channel>
</rss>

