Forum Discussion

clongaw's avatar
clongaw
Frequent Visitor
2 years ago
Solved

Unable to access all needed Credentials for Refresh in OAuth flow in Custom Connector

I have a Custom Connector with a working OAuth flow. I have a working TestConnection function and am able to freely pull data. While trying to create the Refresh function for the flow, I keep running into the following error when I try to access Extension.CurrentCredential() in the Refresh function. 

[9:42:48 AM]	[Info]	RefreshCredential result {
    "Details": "Object reference not set to an instance of an object.",
    "Message": "Exception received during credential refresh.",
    "Status": "Failure"
}

 

I need to have access to the expired AccessToken, the RefreshToken, and a special Code in the function for this particular OAuth flow. I have successfully saved all the information in Extension.CurrentCredentials(), but cannot seem to pass it to the Refresh function.

[9:48:58 AM]	[Info]	ListCredentials result [
    {
        "DataSource": {
            "Kind": "OAuthTest",
            "NormalizedPath": "path",
            "Path": "path"
        },
        "AuthenticationKind": "OAuth2",
        "Properties": {
            "AccessToken": "******",
            "Expires": null,
            "RefreshToken": "******",
            "hResult": "0",
            "code": "code"
        }
    }
]

 

Here is the beginning part of the Refresh function which is causing the error. 

Refresh = (dataSourcePath, refreshToken) => 
    let
        credentials = Extension.CurrentCredential()[Properties],
        oldAccessToken = credentials[AccessToken],
        oldRefreshToken = refreshToken,
        code = credentials[code],

 

I have also tried Extension.CurrentCredential()[access_token], but continue to get the 'Object reference...' error. 

 

  • I have found a solution for my approach and have also seen another option that may be viable. The solution I am using is to set the RefreshToken in credentials as the value "access_token;refresh_token;access_code" as these are the three variables that I need. I do this by taking the TokenResult from Web.Contents and renaming or setting the Record Fields corresponding to access_token and RefreshToken:

    JsonResponse = Json.Document(TokenResult),
    JsonRenamed = Record.RenameFields(JsonResponse, {{"token","access_token"}, {"refreshToken", "RefreshToken"}}) & 
                [RefreshToken = JsonResponse[token] & ";" & JsonResponse[refreshToken] & ";" & code, Expires = 900]
        in
            JsonRenamed;
    

    I can then call Refresh as 

    Refresh = (dataSourcePath, tokens) => 
        let
            TokenList = Text.Split(tokens, ";"),
            accessToken = TokenList{0},
            refreshToken = TokenList{1},
            code = TokenList{2},
       ...
       in  
           output
    

     

    The second option I have seen uses oldCredential to store and access all the old information. This is most likely cleaner, but I have not verified if it works with adding a custom field to the Credentials record to store the special access_code I need. You can see more examples of this technique here: https://community.fabric.microsoft.com/t5/Developer/Custom-Connector-failing-on-refreshing-data-with-multiple-calls/m-p/2608325

1 Reply

  • clongaw's avatar
    clongaw
    Frequent Visitor

    I have found a solution for my approach and have also seen another option that may be viable. The solution I am using is to set the RefreshToken in credentials as the value "access_token;refresh_token;access_code" as these are the three variables that I need. I do this by taking the TokenResult from Web.Contents and renaming or setting the Record Fields corresponding to access_token and RefreshToken:

    JsonResponse = Json.Document(TokenResult),
    JsonRenamed = Record.RenameFields(JsonResponse, {{"token","access_token"}, {"refreshToken", "RefreshToken"}}) & 
                [RefreshToken = JsonResponse[token] & ";" & JsonResponse[refreshToken] & ";" & code, Expires = 900]
        in
            JsonRenamed;
    

    I can then call Refresh as 

    Refresh = (dataSourcePath, tokens) => 
        let
            TokenList = Text.Split(tokens, ";"),
            accessToken = TokenList{0},
            refreshToken = TokenList{1},
            code = TokenList{2},
       ...
       in  
           output
    

     

    The second option I have seen uses oldCredential to store and access all the old information. This is most likely cleaner, but I have not verified if it works with adding a custom field to the Credentials record to store the special access_code I need. You can see more examples of this technique here: https://community.fabric.microsoft.com/t5/Developer/Custom-Connector-failing-on-refreshing-data-with-multiple-calls/m-p/2608325